sbLocalDatabaseMediaListView.cpp
Go to the documentation of this file.
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 
28 
29 #include <DatabaseQuery.h>
30 #include <nsArrayUtils.h>
31 #include <nsAutoLock.h>
32 #include <nsComponentManagerUtils.h>
33 #include <nsServiceManagerUtils.h>
34 #include <nsIClassInfoImpl.h>
35 #include <nsIObjectInputStream.h>
36 #include <nsIObjectOutputStream.h>
37 #include <nsIProgrammingLanguage.h>
38 #include <nsIProperty.h>
39 #include <nsITreeView.h>
40 #include <nsIURI.h>
41 #include <nsIVariant.h>
42 #include <nsIWeakReferenceUtils.h>
43 #include <nsMemory.h>
45 #include <sbICascadeFilterSet.h>
46 #include <sbIDatabaseQuery.h>
47 #include <sbILibrary.h>
48 #include <sbILibraryConstraints.h>
49 #include <sbILocalDatabaseAsyncGUIDArray.h>
50 #include <sbILocalDatabaseSimpleMediaList.h>
51 #include <sbIMediaItem.h>
52 #include <sbIMediaList.h>
53 #include <sbIPropertyInfo.h>
54 #include <sbISQLBuilder.h>
55 #include <sbIMediaList.h>
56 #include <sbLibraryCID.h>
57 #include <sbPropertiesCID.h>
58 #include <sbSQLBuilderCID.h>
59 #include <sbStandardProperties.h>
60 #include <sbStringUtils.h>
62 #include <prlog.h>
63 
65 #include "sbLocalDatabaseCID.h"
67 #include "sbLocalDatabaseLibrary.h"
72 
73 #define DEFAULT_FETCH_SIZE 300
74 
80 {
81 public:
82  explicit
85  : mArray(aArray)
86  , mSelection(aSelection) {
87  mArray->SuppressInvalidation(PR_TRUE);
88  }
89 
91  mArray->SuppressInvalidation(PR_FALSE);
92  mSelection->ConfigurationChanged();
93  }
94 private:
95  nsCOMPtr<sbILocalDatabaseGUIDArray> mArray;
96  nsRefPtr<sbLocalDatabaseMediaListViewSelection> mSelection;
97 };
98 
99 
106  nsIClassInfo,
108 
109 NS_IMPL_CI_INTERFACE_GETTER7(sbLocalDatabaseMediaListView,
116  nsIClassInfo)
117 
122 #ifdef PR_LOGGING
123 static PRLogModuleInfo* sMediaListViewLog = nsnull;
124 #define TRACE(args) if (sMediaListViewLog) PR_LOG(sMediaListViewLog, PR_LOG_DEBUG, args)
125 #define LOG(args) if (sMediaListViewLog) PR_LOG(sMediaListViewLog, PR_LOG_WARN, args)
126 #else /* PR_LOGGING */
127 #define TRACE(args) /* nothing */
128 #define LOG(args) /* nothing */
129 #endif /* PR_LOGGING */
130 
131 /* static */ PLDHashOperator PR_CALLBACK
132 sbLocalDatabaseMediaListView::AddValuesToArrayCallback(nsStringHashKey::KeyType aKey,
133  sbStringArray* aEntry,
134  void* aUserData)
135 {
136  NS_ASSERTION(aEntry, "Null entry in the hash?!");
137  NS_ASSERTION(aUserData, "Null userData!");
138 
139  nsCOMPtr<sbIMutablePropertyArray> propertyArray =
140  static_cast<sbIMutablePropertyArray*>(aUserData);
141  NS_ASSERTION(propertyArray, "Could not cast user data");
142 
143  PRUint32 length = aEntry->Length();
144  nsresult rv;
145  for (PRUint32 i = 0; i < length; i++) {
146  rv = propertyArray->AppendProperty(aKey, aEntry->ElementAt(i));
147  NS_ENSURE_SUCCESS(rv, PL_DHASH_STOP);
148  }
149 
150  return PL_DHASH_NEXT;
151 }
152 
153 /* static */ PLDHashOperator PR_CALLBACK
154 sbLocalDatabaseMediaListView::AddKeysToStringArrayCallback(nsStringHashKey::KeyType aKey,
155  sbStringArray* aEntry,
156  void* aUserData)
157 {
158  NS_ASSERTION(aEntry, "Null entry in the hash?!");
159  NS_ASSERTION(aUserData, "Null userData!");
160 
161  sbStringArray* stringArray = static_cast<sbStringArray*>(aUserData);
162  NS_ASSERTION(stringArray, "Could not cast user data");
163 
164  nsString* appended = stringArray->AppendElement(aKey);
165  NS_ENSURE_TRUE(appended, PL_DHASH_STOP);
166 
167  return PL_DHASH_NEXT;
168 }
169 
175 /* static */ PLDHashOperator PR_CALLBACK
176 sbLocalDatabaseMediaListView::AddListenersToCOMArray(nsISupportsHashKey* aEntry,
177  void* aUserData)
178 {
179  sbViewListenerArray* array = static_cast<sbViewListenerArray*>(aUserData);
180  NS_ASSERTION(array, "Null aUserData!");
181 
182  nsISupports* entry = aEntry->GetKey();
183  NS_ASSERTION(entry, "Null entry in hash!");
184 
185  nsresult rv;
186  nsCOMPtr<sbIMediaListViewListener> listener = do_QueryInterface(entry, &rv);
187  if (NS_FAILED(rv)) {
188  nsWeakPtr maybeWeak = do_QueryInterface(entry, &rv);
189  NS_ASSERTION(NS_SUCCEEDED(rv), "Listener doesn't QI to anything useful!");
190 
191  listener = do_QueryReferent(maybeWeak);
192  if (!listener) {
193  // The listener died or was invalid. Remove it from our table so that we
194  // don't check it again.
195  return PL_DHASH_REMOVE;
196  }
197  }
198 
199  PRBool success = array->AppendObject(listener);
200  NS_ENSURE_TRUE(success, PL_DHASH_STOP);
201 
202  return PL_DHASH_NEXT;
203 }
204 
206  sbLocalDatabaseMediaListBase* aMediaList,
207  nsAString& aDefaultSortProperty,
208  PRUint32 aMediaListId) :
209  mLibrary(aLibrary),
210  mMediaList(aMediaList),
211  mDefaultSortProperty(aDefaultSortProperty),
212  mMediaListId(aMediaListId),
213  mListenerTableLock(nsnull),
214  mInvalidatePending(PR_FALSE)
215 {
216  NS_ASSERTION(aLibrary, "aLibrary is null");
217  NS_ASSERTION(aMediaList, "aMediaList is null");
218 
219  // Build list of properties to ignore when considering whether to invalidate
220  // the view. This is used by ShouldCauseInvalidation
221  mIgnoreSystemProperties.AppendString(NS_LITERAL_STRING(SB_PROPERTY_PLAYCOUNT));
222  mIgnoreSystemProperties.AppendString(NS_LITERAL_STRING(SB_PROPERTY_LASTPLAYTIME));
223  mIgnoreSystemProperties.AppendString(NS_LITERAL_STRING(SB_PROPERTY_SKIPCOUNT));
224  mIgnoreSystemProperties.AppendString(NS_LITERAL_STRING(SB_PROPERTY_LASTSKIPTIME));
225 
226  MOZ_COUNT_CTOR(sbLocalDatabaseMediaListView);
227 #ifdef PR_LOGGING
228  if (!sMediaListViewLog) {
229  sMediaListViewLog = PR_NewLogModule("sbLocalDatabaseMediaListView");
230  }
231 #endif
232  TRACE(("sbLocalDatabaseMediaListView[0x%.8x] - Constructed", this));
233 }
234 
236 {
237  TRACE(("sbLocalDatabaseMediaListView[0x%.8x] - Destructed", this));
238  MOZ_COUNT_DTOR(sbLocalDatabaseMediaListView);
239 
240  if (mMediaList) {
241  nsCOMPtr<sbIMediaListListener> listener =
242  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaListListener*, this));
243  mMediaList->RemoveListener(listener);
244  }
245 
246  if (mCascadeFilterSet) {
247  mCascadeFilterSet->ClearMediaListView();
248  }
249 
250  if (mTreeView) {
251  mTreeView->ClearMediaListView();
252  }
253 
254  if (mListenerTableLock) {
255  nsAutoLock::DestroyLock(mListenerTableLock);
256  }
257 }
258 
259 nsresult
261 {
262 #ifdef DEBUG
263  nsString buff;
264  if (aState) {
265  aState->ToString(buff);
266  }
267  TRACE(("sbLocalDatabaseMediaListView[0x%.8x] - Init %s",
268  this, NS_LossyConvertUTF16toASCII(buff).get()));
269 #endif
270 
271  nsresult rv;
272 
273  nsCOMPtr<sbILocalDatabaseMediaListViewState> state;
274  if (aState) {
275  state = do_QueryInterface(aState, &rv);
276  NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
277  }
278 
279  PRBool success = mListenerTable.Init();
280  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
281 
282  mListenerTableLock =
283  nsAutoLock::NewLock("sbLocalDatabaseMediaListView::mListenerTableLock");
284  NS_ENSURE_TRUE(mListenerTableLock, NS_ERROR_OUT_OF_MEMORY);
285 
286  mPropMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
287  NS_ENSURE_SUCCESS(rv, rv);
288 
289  mArray = do_CreateInstance(SB_LOCALDATABASE_ASYNCGUIDARRAY_CONTRACTID, &rv);
290 
291  nsAutoString databaseGuid;
292  rv = mLibrary->GetDatabaseGuid(databaseGuid);
293  NS_ENSURE_SUCCESS(rv, rv);
294 
295  rv = mArray->SetDatabaseGUID(databaseGuid);
296  NS_ENSURE_SUCCESS(rv, rv);
297 
298  nsCOMPtr<sbILocalDatabasePropertyCache> propertyCache;
299  rv = mLibrary->GetPropertyCache(getter_AddRefs(propertyCache));
300  NS_ENSURE_SUCCESS(rv, rv);
301 
302  rv = mArray->SetPropertyCache(propertyCache);
303  NS_ENSURE_SUCCESS(rv, rv);
304 
305  nsCOMPtr<nsIURI> databaseLocation;
306  rv = mLibrary->GetDatabaseLocation(getter_AddRefs(databaseLocation));
307  NS_ENSURE_SUCCESS(rv, rv);
308 
309  if (databaseLocation) {
310  rv = mArray->SetDatabaseLocation(databaseLocation);
311  NS_ENSURE_SUCCESS(rv, rv);
312  }
313 
314  if (mMediaListId == 0) {
315  rv = mArray->SetBaseTable(NS_LITERAL_STRING("media_items"));
316  NS_ENSURE_SUCCESS(rv, rv);
317  }
318  else {
319  rv = mArray->SetBaseTable(NS_LITERAL_STRING("simple_media_lists"));
320  NS_ENSURE_SUCCESS(rv, rv);
321 
322  rv = mArray->SetBaseConstraintColumn(NS_LITERAL_STRING("media_item_id"));
323  NS_ENSURE_SUCCESS(rv, rv);
324 
325  rv = mArray->SetBaseConstraintValue(mMediaListId);
326  NS_ENSURE_SUCCESS(rv, rv);
327  }
328 
329  nsCOMPtr<sbILocalDatabaseGUIDArrayLengthCache> lengthCache;
330  rv = mLibrary->GetLengthCache(getter_AddRefs(lengthCache));
331  NS_ENSURE_SUCCESS(rv, rv);
332 
333  rv = mArray->SetLengthCache(lengthCache);
334  NS_ENSURE_SUCCESS(rv, rv);
335 
336  rv = mArray->SetFetchSize(DEFAULT_FETCH_SIZE);
337  NS_ENSURE_SUCCESS(rv, rv);
338 
339  rv = CreateQueries();
340  NS_ENSURE_SUCCESS(rv, rv);
341 
342  nsRefPtr<sbLocalDatabaseMediaListViewSelectionState> selectionState;
343  nsCOMPtr<sbIMutablePropertyArray> sort;
344  if (state) {
345  rv = state->GetSort(getter_AddRefs(sort));
346  NS_ENSURE_SUCCESS(rv, rv);
347 
348  rv = state->GetSearch(getter_AddRefs(mViewSearch));
349  NS_ENSURE_SUCCESS(rv, rv);
350 
351  rv = state->GetFilter(getter_AddRefs(mViewFilter));
352  NS_ENSURE_SUCCESS(rv, rv);
353 
354  rv = state->GetSelection(getter_AddRefs(selectionState));
355  NS_ENSURE_SUCCESS(rv, rv);
356  }
357 
358  nsString guid;
359  rv = mMediaList->GetGuid(guid);
360  NS_ENSURE_SUCCESS(rv, rv);
361 
362  nsCOMPtr<sbILocalDatabaseGUIDArray> syncArray =
363  do_QueryInterface(mArray, &rv);
364  NS_ENSURE_SUCCESS(rv, rv);
365 
366  mSelection = new sbLocalDatabaseMediaListViewSelection();
367  NS_ENSURE_TRUE(mSelection, NS_ERROR_OUT_OF_MEMORY);
368  rv = mSelection->Init(mLibrary,
369  guid,
370  syncArray,
371  mMediaListId == 0,
372  selectionState);
373  NS_ENSURE_SUCCESS(rv, rv);
374 
375  rv = SetSortInternal(sort);
376  NS_ENSURE_SUCCESS(rv, rv);
377 
378  rv = mSelection->ConfigurationChanged();
379  NS_ENSURE_SUCCESS(rv, rv);
380 
381  // Restore cfs and tree state
382  if (state) {
383  nsRefPtr<sbLocalDatabaseCascadeFilterSetState> filterSetState;
384  rv = state->GetFilterSet(getter_AddRefs(filterSetState));
385  NS_ENSURE_SUCCESS(rv, rv);
386 
387  if (filterSetState) {
388  nsCOMPtr<sbILocalDatabaseAsyncGUIDArray> guidArray;
389  rv = mArray->CloneAsyncArray(getter_AddRefs(guidArray));
390  NS_ENSURE_SUCCESS(rv, rv);
391 
392  nsRefPtr<sbLocalDatabaseCascadeFilterSet> filterSet =
394  NS_ENSURE_TRUE(filterSet, NS_ERROR_OUT_OF_MEMORY);
395 
396  rv = filterSet->Init(mLibrary, guidArray, filterSetState);
397  NS_ENSURE_SUCCESS(rv, rv);
398 
399  mCascadeFilterSet = filterSet;
400  }
401 
402  nsRefPtr<sbLocalDatabaseTreeViewState> treeViewState;
403  rv = state->GetTreeViewState(getter_AddRefs(treeViewState));
404  if (treeViewState) {
405  nsRefPtr<sbLocalDatabaseTreeView> tree = new sbLocalDatabaseTreeView();
406  NS_ENSURE_TRUE(tree, NS_ERROR_OUT_OF_MEMORY);
407 
408  rv = tree->Init(this, mArray, nsnull, treeViewState);
409  NS_ENSURE_SUCCESS(rv, rv);
410 
411  mTreeView = tree;
412  }
413  }
414 
415  return NS_OK;
416 }
417 
418 already_AddRefed<sbLocalDatabaseMediaListBase>
420 {
421  NS_ASSERTION(mMediaList, "mMediaList is null");
422  sbLocalDatabaseMediaListBase* result = mMediaList;
423  NS_ADDREF(result);
424  return result;
425 }
426 
429 {
430  NS_ASSERTION(mArray, "mArray is null");
431  return mArray;
432 }
433 
434 NS_IMETHODIMP
435 sbLocalDatabaseMediaListView::GetMediaList(sbIMediaList** aMediaList)
436 {
437  NS_ENSURE_ARG_POINTER(aMediaList);
438 
439  NS_IF_ADDREF(*aMediaList = mMediaList);
440  return NS_OK;
441 }
442 
443 NS_IMETHODIMP
444 sbLocalDatabaseMediaListView::GetLength(PRUint32* aFilteredLength)
445 {
446  NS_ENSURE_ARG_POINTER(aFilteredLength);
447 
448  nsresult rv = mArray->GetLength(aFilteredLength);
449  NS_ENSURE_SUCCESS(rv, rv);
450 
451  return NS_OK;
452 }
453 
454 NS_IMETHODIMP
455 sbLocalDatabaseMediaListView::GetTreeView(nsITreeView** aTreeView)
456 {
457 
458  NS_ENSURE_ARG_POINTER(aTreeView);
459 
460  if (!mTreeView) {
461 
462  nsresult rv;
463 
464  nsRefPtr<sbLocalDatabaseTreeView> tree = new sbLocalDatabaseTreeView();
465  NS_ENSURE_TRUE(tree, NS_ERROR_OUT_OF_MEMORY);
466 
467  rv = tree->Init(this, mArray, mViewSort, nsnull);
468  NS_ENSURE_SUCCESS(rv, rv);
469 
470  mTreeView = tree;
471  }
472 
473  NS_ADDREF(*aTreeView = mTreeView);
474 
475  return NS_OK;
476 }
477 
478 NS_IMETHODIMP
479 sbLocalDatabaseMediaListView::GetCascadeFilterSet(sbICascadeFilterSet** aCascadeFilterSet)
480 {
481  NS_ENSURE_ARG_POINTER(aCascadeFilterSet);
482 
483  nsresult rv;
484 
485  if (!mCascadeFilterSet) {
486  nsCOMPtr<sbILocalDatabaseAsyncGUIDArray> guidArray;
487  rv = mArray->CloneAsyncArray(getter_AddRefs(guidArray));
488  NS_ENSURE_SUCCESS(rv, rv);
489 
490  nsRefPtr<sbLocalDatabaseCascadeFilterSet> filterSet =
492  NS_ENSURE_TRUE(filterSet, NS_ERROR_OUT_OF_MEMORY);
493 
494  rv = filterSet->Init(mLibrary, guidArray, nsnull);
495  NS_ENSURE_SUCCESS(rv, rv);
496 
497  mCascadeFilterSet = filterSet;
498  }
499 
500  NS_ADDREF(*aCascadeFilterSet = mCascadeFilterSet);
501  return NS_OK;
502 }
503 
504 NS_IMETHODIMP
505 sbLocalDatabaseMediaListView::GetItemByIndex(PRUint32 aIndex,
506  sbIMediaItem** _retval)
507 {
508  NS_ENSURE_ARG_POINTER(_retval);
509 
510  nsresult rv;
511 
512  nsAutoString guid;
513  rv = mArray->GetGuidByIndex(aIndex, guid);
514  NS_ENSURE_SUCCESS(rv, rv);
515 
516  nsCOMPtr<sbIMediaItem> item;
517  rv = mLibrary->GetMediaItem(guid, getter_AddRefs(item));
518  NS_ENSURE_SUCCESS(rv, rv);
519 
520  NS_ADDREF(*_retval = item);
521  return NS_OK;
522 }
523 
524 NS_IMETHODIMP
525 sbLocalDatabaseMediaListView::GetIndexForItem(sbIMediaItem* aMediaItem,
526  PRUint32* _retval)
527 {
528  NS_ENSURE_ARG_POINTER(aMediaItem);
529  NS_ENSURE_ARG_POINTER(_retval);
530 
531  nsAutoString guid;
532  nsresult rv = aMediaItem->GetGuid(guid);
533  NS_ENSURE_SUCCESS(rv, rv);
534 
535  rv = mArray->GetFirstIndexByGuid(guid, _retval);
536  if (rv == NS_ERROR_NOT_AVAILABLE) {
537  return rv;
538  }
539  NS_ENSURE_SUCCESS(rv, rv);
540 
541  return NS_OK;
542 }
543 
544 NS_IMETHODIMP
545 sbLocalDatabaseMediaListView::GetUnfilteredIndex(PRUint32 aIndex,
546  PRUint32* _retval)
547 {
548  NS_ENSURE_ARG_POINTER(_retval);
549 
550  nsresult rv;
551 
552  // If this view is on the library, we know we only have unique items so
553  // we can get the guid of the item at the given index and use that to find
554  // the unfiltered index
555  if (mMediaListId == 0) {
556  nsAutoString guid;
557  rv = mArray->GetGuidByIndex(aIndex, guid);
558  NS_ENSURE_SUCCESS(rv, rv);
559 
560  nsCOMPtr<sbIMediaItem> item;
561  rv = mMediaList->GetItemByGuid(guid, getter_AddRefs(item));
562  NS_ENSURE_SUCCESS(rv, rv);
563 
564  rv = mMediaList->IndexOf(item, 0, _retval);
565  NS_ENSURE_SUCCESS(rv, rv);
566 
567  }
568  else {
569  // Otherwise, get the ordinal for this item and use it to get the item
570  // from the full media list
571  nsAutoString ordinal;
572  rv = mArray->GetOrdinalByIndex(aIndex, ordinal);
573  NS_ENSURE_SUCCESS(rv, rv);
574 
575  nsCOMPtr<sbILocalDatabaseSimpleMediaList> sml =
576  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaList*, mMediaList), &rv);
577  NS_ENSURE_SUCCESS(rv, rv);
578 
579  rv = sml->GetIndexByOrdinal(ordinal, _retval);
580  NS_ENSURE_SUCCESS(rv, rv);
581  }
582 
583  return NS_OK;
584 }
585 
586 NS_IMETHODIMP
587 sbLocalDatabaseMediaListView::GetViewItemUIDForIndex(PRUint32 aIndex,
588  nsAString& _retval)
589 {
590  nsresult rv;
591 
592  PRUint64 rowid;
593  rv = mArray->GetRowidByIndex(aIndex, &rowid);
594  NS_ENSURE_SUCCESS(rv, rv);
595 
596  PRUint32 mediaItemid;
597  rv = mArray->GetMediaItemIdByIndex(aIndex, &mediaItemid);
598  NS_ENSURE_SUCCESS(rv, rv);
599 
600  // the ViewItemUID is a concatenation of the mediaitemid and rowid
601  _retval.Truncate();
602  AppendInt(_retval, rowid);
603  _retval.Append('-');
604  _retval.AppendInt(mediaItemid);
605 
606  return NS_OK;
607 }
608 
609 NS_IMETHODIMP
610 sbLocalDatabaseMediaListView::GetIndexForViewItemUID(const nsAString& aViewItemUID,
611  PRUint32* _retval)
612 {
613  NS_ENSURE_ARG_POINTER(_retval);
614 
615  nsresult rv;
616 
617  rv = mArray->GetIndexByViewItemUID(aViewItemUID, _retval);
618  NS_ENSURE_SUCCESS(rv, rv);
619 
620  return NS_OK;
621 }
622 
623 NS_IMETHODIMP
624 sbLocalDatabaseMediaListView::GetDistinctValuesForProperty(const nsAString& aPropertyID,
625  nsIStringEnumerator** _retval)
626 {
627  NS_ENSURE_ARG_POINTER(_retval);
628 
629  nsCOMPtr<sbILocalDatabaseGUIDArray> guidArray;
630  nsresult rv = mArray->Clone(getter_AddRefs(guidArray));
631  NS_ENSURE_SUCCESS(rv, rv);
632 
633  rv = guidArray->SetIsDistinct(PR_TRUE);
634  NS_ENSURE_SUCCESS(rv, rv);
635 
636  rv = guidArray->ClearSorts();
637  NS_ENSURE_SUCCESS(rv, rv);
638 
639  rv = guidArray->AddSort(aPropertyID, PR_TRUE);
640  NS_ENSURE_SUCCESS(rv, rv);
641 
642  sbGUIDArrayValueEnumerator* enumerator =
643  new sbGUIDArrayValueEnumerator(guidArray);
644  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
645 
646  NS_ADDREF(*_retval = enumerator);
647  return NS_OK;
648 }
649 
650 NS_IMETHODIMP
651 sbLocalDatabaseMediaListView::Clone(sbIMediaListView** _retval)
652 {
653  NS_ENSURE_ARG_POINTER(_retval);
654 
655  nsresult rv;
656 
657  nsCOMPtr<sbIMediaListViewState> state;
658  rv = GetState(getter_AddRefs(state));
659  NS_ENSURE_SUCCESS(rv, rv);
660 
661  nsRefPtr<sbLocalDatabaseMediaListView>
662  clone(new sbLocalDatabaseMediaListView(mLibrary,
663  mMediaList,
664  mDefaultSortProperty,
665  mMediaListId));
666  NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
667 
668  rv = clone->Init(state);
669  NS_ENSURE_SUCCESS(rv, rv);
670 
671  NS_ADDREF(*_retval = clone);
672  return NS_OK;
673 }
674 
675 NS_IMETHODIMP
676 sbLocalDatabaseMediaListView::GetState(sbIMediaListViewState** _retval)
677 {
678  NS_ENSURE_ARG_POINTER(_retval);
679 
680  nsresult rv;
681 
682  nsCOMPtr<sbIMutablePropertyArray> sort;
683  rv = ClonePropertyArray(mViewSort, getter_AddRefs(sort));
684  NS_ENSURE_SUCCESS(rv, rv);
685 
686  nsRefPtr<sbLocalDatabaseMediaListViewSelectionState> selectionState;
687  rv = mSelection->GetState(getter_AddRefs(selectionState));
688  NS_ENSURE_SUCCESS(rv, rv);
689 
690  nsRefPtr<sbLocalDatabaseCascadeFilterSetState> filterSetState;
691  if (mCascadeFilterSet) {
692  rv = mCascadeFilterSet->GetState(getter_AddRefs(filterSetState));
693  NS_ENSURE_SUCCESS(rv, rv);
694  }
695 
696  nsRefPtr<sbLocalDatabaseTreeViewState> treeViewState;
697  if (mTreeView) {
698  rv = mTreeView->GetState(getter_AddRefs(treeViewState));
699  NS_ENSURE_SUCCESS(rv, rv);
700  }
701 
702  nsRefPtr<sbLocalDatabaseMediaListViewState> state =
704  mViewSearch,
705  mViewFilter,
706  selectionState,
707  filterSetState,
708  treeViewState);
709  NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
710  NS_ADDREF(*_retval = state);
711 
712  return NS_OK;
713 }
714 
715 NS_IMETHODIMP
716 sbLocalDatabaseMediaListView::AddListener(sbIMediaListViewListener* aListener,
717  /* optional */ PRBool aOwnsWeak)
718 {
719  NS_ENSURE_ARG_POINTER(aListener);
720 
721  nsresult rv;
722  nsCOMPtr<nsISupports> supports = do_QueryInterface(aListener, &rv);
723  NS_ENSURE_SUCCESS(rv, rv);
724 
725  if (aOwnsWeak) {
726  nsWeakPtr weakRef = do_GetWeakReference(supports, &rv);
727  NS_ENSURE_SUCCESS(rv, rv);
728 
729  supports = do_QueryInterface(weakRef, &rv);
730  NS_ENSURE_SUCCESS(rv, rv);
731  }
732 
733  nsAutoLock lock(mListenerTableLock);
734  if (mListenerTable.GetEntry(supports)) {
735  NS_WARNING("Attempted to add the same listener twice!");
736  return NS_OK;
737  }
738 
739  if (!mListenerTable.PutEntry(supports)) {
740  NS_WARNING("Failed to add entry to listener table");
741  return NS_ERROR_FAILURE;
742  }
743  return NS_OK;
744 }
745 
746 NS_IMETHODIMP
747 sbLocalDatabaseMediaListView::RemoveListener(sbIMediaListViewListener* aListener)
748 {
749  NS_ENSURE_ARG_POINTER(aListener);
750 
751  nsresult rv;
752  nsCOMPtr<nsISupports> supports = do_QueryInterface(aListener, &rv);
753  NS_ENSURE_SUCCESS(rv, rv);
754 
755  nsCOMPtr<nsISupports> weakSupports;
756 
757  // Test to see if the listener supports weak references *and* a weak reference
758  // is in our table. If both conditions are met then that is the listener that
759  // will be removed. Otherwise remove a strong listener.
760  nsCOMPtr<nsISupportsWeakReference> maybeWeak = do_QueryInterface(supports, &rv);
761  if (NS_SUCCEEDED(rv)) {
762  nsWeakPtr weakRef = do_GetWeakReference(supports, &rv);
763  NS_ENSURE_SUCCESS(rv, rv);
764 
765  weakSupports = do_QueryInterface(weakRef, &rv);
766  NS_ENSURE_SUCCESS(rv, rv);
767  }
768 
769  nsAutoLock lock(mListenerTableLock);
770 
771  // Lame, but we have to check this inside the lock.
772  if (weakSupports && mListenerTable.GetEntry(weakSupports)) {
773  supports = weakSupports;
774  }
775 
776  NS_WARN_IF_FALSE(mListenerTable.GetEntry(supports),
777  "Attempted to remove a listener that was never added!");
778 
779  mListenerTable.RemoveEntry(supports);
780 
781  return NS_OK;
782 }
783 
784 NS_IMETHODIMP
786 {
787  NS_ENSURE_ARG_POINTER(aSelection);
788  NS_IF_ADDREF(*aSelection = mSelection);
789  return NS_OK;
790 }
791 
792 NS_IMETHODIMP
793 sbLocalDatabaseMediaListView::RemoveSelectedMediaItems()
794 {
795  nsString viewContentType;
796 
797  PRUint32 viewLength = 0;
798  nsresult rv = GetLength(&viewLength);
799  NS_ENSURE_SUCCESS(rv, rv);
800 
801  PRInt32 selectionLength = 0;
802  rv = mSelection->GetCount(&selectionLength);
803  NS_ENSURE_SUCCESS(rv, rv);
804 
805  PRUint32 filterCount = 0;
806  if(mViewFilter) {
807  rv = mViewFilter->GetGroupCount(&filterCount);
808  NS_ENSURE_SUCCESS(rv, rv);
809 
810  // Check to see if the only filters are 'hidden' and 'is_list'.
811  // If that is the case, we can pretend like there are no filters.
812  if(filterCount) {
813  nsCOMPtr<sbILibraryConstraintBuilder> audioViewBuilder =
814  do_CreateInstance(SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID, &rv);
815  NS_ENSURE_SUCCESS(rv, rv);
816 
817  rv = audioViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
818  NS_LITERAL_STRING("0"),
819  nsnull);
820  NS_ENSURE_SUCCESS(rv, rv);
821 
822  rv = audioViewBuilder->Intersect(nsnull);
823  NS_ENSURE_SUCCESS(rv, rv);
824 
825  rv = audioViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
826  NS_LITERAL_STRING("0"),
827  nsnull);
828  NS_ENSURE_SUCCESS(rv, rv);
829 
830  rv = audioViewBuilder->Intersect(nsnull);
831  NS_ENSURE_SUCCESS(rv, rv);
832 
833  rv = audioViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE),
834  NS_LITERAL_STRING("audio"),
835  nsnull);
836  NS_ENSURE_SUCCESS(rv, rv);
837 
838  nsCOMPtr<sbILibraryConstraintBuilder> videoViewBuilder =
839  do_CreateInstance(SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID, &rv);
840  NS_ENSURE_SUCCESS(rv, rv);
841 
842  rv = videoViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_ISLIST),
843  NS_LITERAL_STRING("0"),
844  nsnull);
845  NS_ENSURE_SUCCESS(rv, rv);
846 
847  rv = videoViewBuilder->Intersect(nsnull);
848  NS_ENSURE_SUCCESS(rv, rv);
849 
850  rv = videoViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_HIDDEN),
851  NS_LITERAL_STRING("0"),
852  nsnull);
853  NS_ENSURE_SUCCESS(rv, rv);
854 
855  rv = videoViewBuilder->Intersect(nsnull);
856  NS_ENSURE_SUCCESS(rv, rv);
857 
858  rv = videoViewBuilder->Include(NS_LITERAL_STRING(SB_PROPERTY_CONTENTTYPE),
859  NS_LITERAL_STRING("video"),
860  nsnull);
861  NS_ENSURE_SUCCESS(rv, rv);
862 
863  nsCOMPtr<sbILibraryConstraint> audioViewConstraint;
864  rv = audioViewBuilder->Get(getter_AddRefs(audioViewConstraint));
865  NS_ENSURE_SUCCESS(rv, rv);
866 
867  nsCOMPtr<sbILibraryConstraint> videoViewConstraint;
868  rv = videoViewBuilder->Get(getter_AddRefs(videoViewConstraint));
869  NS_ENSURE_SUCCESS(rv, rv);
870 
871  PRBool isEqualToAudioView = PR_FALSE;
872  rv = mViewFilter->Equals(audioViewConstraint, &isEqualToAudioView);
873  NS_ENSURE_SUCCESS(rv, rv);
874 
875  PRBool isEqualToVideoView = PR_FALSE;
876  rv = mViewFilter->Equals(videoViewConstraint, &isEqualToVideoView);
877  NS_ENSURE_SUCCESS(rv, rv);
878 
879  if(isEqualToAudioView) {
880  filterCount = 0;
881  viewContentType = NS_LITERAL_STRING("audio");
882  }
883  else if(isEqualToVideoView) {
884  filterCount = 0;
885  viewContentType = NS_LITERAL_STRING("video");
886  }
887  }
888  }
889 
890  PRUint32 searchCount = 0;
891  if(mViewSearch) {
892  rv = mViewSearch->GetGroupCount(&searchCount);
893  NS_ENSURE_SUCCESS(rv, rv);
894  }
895 
896  PRBool cfsIsFiltering = PR_FALSE;
897 
898  if(mCascadeFilterSet) {
899  PRUint16 cfsCount = 0;
900  rv = mCascadeFilterSet->GetLength(&cfsCount);
901  NS_ENSURE_SUCCESS(rv, rv);
902 
903  for(PRUint16 current = 0; current < cfsCount; ++current) {
904  nsCOMPtr<nsIArray> filterConfig;
905 
906  rv = mCascadeFilterSet->Get(current, getter_AddRefs(filterConfig));
907  NS_ENSURE_SUCCESS(rv, rv);
908 
909  PRUint32 length = 0;
910  rv = filterConfig->GetLength(&length);
911  NS_ENSURE_SUCCESS(rv, rv);
912 
913  if(length > 0) {
914  cfsIsFiltering = PR_TRUE;
915  break;
916  }
917  }
918  }
919 
920  PRBool isSelected = PR_FALSE;
921  PRInt32 currentIndex;
922  rv = mSelection->GetCurrentIndex(&currentIndex);
923  NS_ENSURE_SUCCESS(rv, rv);
924 
925  rv = mSelection->IsIndexSelected(currentIndex, &isSelected);
926  NS_ENSURE_SUCCESS(rv, rv);
927 
928  // The user is removing all of the tracks from the view,
929  // use clear instead. It's very important to check for filter
930  // and search counts here otherwise we may clear the library
931  // because the user has selected everything in the view when
932  // it's in a filtered or has a search applied!
933  if((PRInt32(viewLength) == selectionLength) && !filterCount && !searchCount && !cfsIsFiltering) {
934  // If it's a library, call clear items instead. We do this so
935  // that all playlists the user has are preserved.
936  if(mMediaListId == 0) {
937  rv = mLibrary->ClearItemsByType(viewContentType);
938  NS_ENSURE_SUCCESS(rv, rv);
939  }
940  else {
941  // Plain medialists just get cleared.
942  rv = mMediaList->Clear();
943  NS_ENSURE_SUCCESS(rv, rv);
944  }
945  }
946  else {
947  nsCOMPtr<nsISimpleEnumerator> selection;
948  rv = mSelection->GetSelectedIndexedMediaItems(getter_AddRefs(selection));
949  NS_ENSURE_SUCCESS(rv, rv);
950 
951  rv = mLibrary->RemoveSelected(selection, this);
952  NS_ENSURE_SUCCESS(rv, rv);
953  }
954 
955  // Invalidate current selection as it has been removed
956  if (isSelected) {
957  rv = mSelection->SelectNone();
958  NS_ENSURE_SUCCESS(rv, rv);
959  }
960 
961  return NS_OK;
962 }
963 
964 nsresult
965 sbLocalDatabaseMediaListView::ClonePropertyArray(sbIPropertyArray* aSource,
966  sbIMutablePropertyArray** _retval)
967 {
968  NS_ENSURE_ARG_POINTER(aSource);
969  NS_ENSURE_ARG_POINTER(_retval);
970 
971  nsresult rv;
972 
973  nsCOMPtr<sbIMutablePropertyArray> clone =
974  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
975  NS_ENSURE_SUCCESS(rv, rv);
976 
977  PRBool strict;
978  rv = aSource->GetValidated(&strict);
979  NS_ENSURE_SUCCESS(rv, rv);
980 
981  rv = clone->SetStrict(strict);
982  NS_ENSURE_SUCCESS(rv, rv);
983 
984  PRUint32 propertyCount;
985  rv = aSource->GetLength(&propertyCount);
986  NS_ENSURE_SUCCESS(rv, rv);
987 
988  for (PRUint32 i = 0; i < propertyCount; i++) {
989  nsCOMPtr<sbIProperty> property;
990  rv = aSource->GetPropertyAt(i, getter_AddRefs(property));
991  NS_ENSURE_SUCCESS(rv, rv);
992 
993  nsString propertyID;
994  rv = property->GetId(propertyID);
995  NS_ENSURE_SUCCESS(rv, rv);
996 
997  nsString value;
998  rv = property->GetValue(value);
999  NS_ENSURE_SUCCESS(rv, rv);
1000 
1001  rv = clone->AppendProperty(propertyID, value);
1002  NS_ENSURE_SUCCESS(rv, rv);
1003  }
1004 
1005  NS_ADDREF(*_retval = clone);
1006  return NS_OK;
1007 }
1008 
1009 nsresult
1010 sbLocalDatabaseMediaListView::HasCommonProperty(sbIPropertyArray* aBag1,
1011  sbIPropertyArray* aBag2,
1012  nsStringArray * aPropertiesToIgnore,
1013  PRBool* aHasCommonProperty)
1014 {
1015  NS_ASSERTION(aBag1, "aBag1 is null");
1016  NS_ASSERTION(aBag2, "aBag2 is null");
1017  NS_ASSERTION(aHasCommonProperty, "aHasCommonProperty is null");
1018 
1019  PRUint32 length;
1020  nsresult rv = aBag1->GetLength(&length);
1021  NS_ENSURE_SUCCESS(rv, rv);
1022 
1023  for (PRUint32 i = 0; i < length; i++) {
1024  nsCOMPtr<sbIProperty> property;
1025  rv = aBag1->GetPropertyAt(i, getter_AddRefs(property));
1026  NS_ENSURE_SUCCESS(rv, rv);
1027 
1028  nsString propertyID;
1029  rv = property->GetId(propertyID);
1030  NS_ENSURE_SUCCESS(rv, rv);
1031 
1032  // Only compare if we're not ignoring this property
1033  if (!aPropertiesToIgnore ||
1034  aPropertiesToIgnore->IndexOf(propertyID) == -1) {
1035  nsString junk;
1036  rv = aBag2->GetPropertyValue(propertyID, junk);
1037  if (NS_SUCCEEDED(rv)) {
1038  *aHasCommonProperty = PR_TRUE;
1039  return NS_OK;
1040  }
1041  }
1042  }
1043 
1044  *aHasCommonProperty = PR_FALSE;
1045  return NS_OK;
1046 }
1047 
1048 nsresult
1049 sbLocalDatabaseMediaListView::HasCommonProperty(sbIPropertyArray* aBag,
1051  PRBool* aHasCommonProperty)
1052 {
1053  NS_ASSERTION(aBag, "aBag is null");
1054  NS_ASSERTION(aConstraint, "aConstraint is null");
1055  NS_ASSERTION(aHasCommonProperty, "aHasCommonProperty is null");
1056 
1057  PRUint32 length;
1058  nsresult rv = aBag->GetLength(&length);
1059  NS_ENSURE_SUCCESS(rv, rv);
1060 
1061  for (PRUint32 i = 0; i < length; i++) {
1062  nsCOMPtr<sbIProperty> property;
1063  rv = aBag->GetPropertyAt(i, getter_AddRefs(property));
1064  NS_ENSURE_SUCCESS(rv, rv);
1065 
1066  nsString propertyID;
1067  rv = property->GetId(propertyID);
1068  NS_ENSURE_SUCCESS(rv, rv);
1069 
1070  PRUint32 groupCount;
1071  rv = aConstraint->GetGroupCount(&groupCount);
1072  NS_ENSURE_SUCCESS(rv, rv);
1073 
1074  for (PRUint32 j = 0; j < groupCount; j++) {
1075  nsCOMPtr<sbILibraryConstraintGroup> group;
1076  rv = aConstraint->GetGroup(j, getter_AddRefs(group));
1077  NS_ENSURE_SUCCESS(rv, rv);
1078 
1079  PRBool hasProperty;
1080  rv = group->HasProperty(propertyID, &hasProperty);
1081  NS_ENSURE_SUCCESS(rv, rv);
1082 
1083  if (hasProperty) {
1084  *aHasCommonProperty = PR_TRUE;
1085  return NS_OK;
1086  }
1087  }
1088  }
1089 
1090  *aHasCommonProperty = PR_FALSE;
1091 
1092  return NS_OK;
1093 }
1094 
1095 nsresult
1096 sbLocalDatabaseMediaListView::ShouldCauseInvalidation(sbIPropertyArray* aProperties,
1097  PRBool* aShouldCauseInvalidation)
1098 {
1099  NS_ASSERTION(aProperties, "aProperties is null");
1100  NS_ASSERTION(aShouldCauseInvalidation, "aShouldCauseInvalidation is null");
1101  nsresult rv;
1102 
1103  PRBool hasCommon;
1104  *aShouldCauseInvalidation = PR_TRUE;
1105 
1106  // If one of the updated properties is involved in the current filter,
1107  // or search, we should invalidate
1108 
1109  // Search sort
1110  nsCOMPtr<sbIPropertyArray> props;
1111  rv = GetCurrentSort(getter_AddRefs(props));
1112  NS_ENSURE_SUCCESS(rv, rv);
1113  rv = HasCommonProperty(aProperties, props, &mIgnoreSystemProperties, &hasCommon);
1114  NS_ENSURE_SUCCESS(rv, rv);
1115  if (hasCommon) {
1116  return NS_OK;
1117  }
1118 
1119  // Search secondary sort
1120  nsCOMPtr<sbIProperty> property = nsnull;
1121  rv = props->GetPropertyAt(0, getter_AddRefs(property));
1122  if (NS_SUCCEEDED(rv) && property) {
1123  nsString propertyID;
1124  rv = property->GetId(propertyID);
1125  NS_ENSURE_SUCCESS(rv, rv);
1126 
1127  nsCOMPtr<sbIPropertyInfo> propertyInfo;
1128  nsCOMPtr<sbIPropertyManager> propertyManager =
1129  do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
1130  NS_ENSURE_SUCCESS(rv, rv);
1131  rv = propertyManager->GetPropertyInfo(propertyID,
1132  getter_AddRefs(propertyInfo));
1133  NS_ENSURE_SUCCESS(rv, rv);
1134 
1135  nsCOMPtr<sbIPropertyArray> secondarySort = nsnull;
1136  rv = propertyInfo->GetSecondarySort(getter_AddRefs(secondarySort));
1137 
1138  if (NS_SUCCEEDED(rv) && secondarySort) {
1139  rv = HasCommonProperty(aProperties, secondarySort, &mIgnoreSystemProperties, &hasCommon);
1140  NS_ENSURE_SUCCESS(rv, rv);
1141  if (hasCommon) {
1142  return NS_OK;
1143  }
1144  }
1145  }
1146 
1147  // Search filter
1148  nsCOMPtr<sbILibraryConstraint> filter;
1149  rv = GetFilterConstraint(getter_AddRefs(filter));
1150  NS_ENSURE_SUCCESS(rv, rv);
1151  if (filter) {
1152  rv = HasCommonProperty(aProperties, filter, &hasCommon);
1153  NS_ENSURE_SUCCESS(rv, rv);
1154  if (hasCommon) {
1155  return NS_OK;
1156  }
1157  }
1158 
1159  // Search search
1160  nsCOMPtr<sbILibraryConstraint> search;
1161  rv = GetSearchConstraint(getter_AddRefs(search));
1162  NS_ENSURE_SUCCESS(rv, rv);
1163  if (search) {
1164  rv = HasCommonProperty(aProperties, search, &hasCommon);
1165  NS_ENSURE_SUCCESS(rv, rv);
1166  if (hasCommon) {
1167  return NS_OK;
1168  }
1169  }
1170 
1171  *aShouldCauseInvalidation = PR_FALSE;
1172 
1173  return NS_OK;
1174 }
1175 
1176 nsresult
1177 sbLocalDatabaseMediaListView::UpdateListener(PRBool aRemoveListener)
1178 {
1179  nsresult rv;
1180 
1181  nsCOMPtr<sbIMediaListListener> listener =
1182  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediaListListener*, this));
1183 
1184  if (aRemoveListener) {
1185  rv = mMediaList->RemoveListener(listener);
1186  return rv;
1187  }
1188 
1189 /*
1190  XXXsteve Once we fix bug 3875 we can use this code :)
1191 
1192  nsCOMPtr<sbIMutablePropertyArray> filter =
1193  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1194  NS_ENSURE_SUCCESS(rv, rv);
1195 
1196  rv = ClonePropertyArray(mViewSort, getter_AddRefs(filter));
1197  NS_ENSURE_SUCCESS(rv, rv);
1198 
1199  // Also add the ordinal to the filter so we get notified when the list
1200  // is reordered
1201  rv = filter->AppendProperty(NS_LITERAL_STRING(SB_PROPERTY_ORDINAL),
1202  EmptyString());
1203  NS_ENSURE_SUCCESS(rv, rv);
1204 */
1205  rv = mMediaList->AddListener(listener,
1206  PR_TRUE,
1208  nsnull);
1209  return rv;
1210 }
1211 
1212 void
1213 sbLocalDatabaseMediaListView::NotifyListenersInternal(ListenerFunc aListenerFunc)
1214 {
1215  sbViewListenerArray listeners;
1216  {
1217  // Take a snapshot of the listener array. This will return only strong
1218  // references, so any weak refs that have died will not be included in this
1219  // list.
1220  nsAutoLock lock(mListenerTableLock);
1221  mListenerTable.EnumerateEntries(AddListenersToCOMArray, &listeners);
1222  }
1223 
1224  sbIMediaListView* thisPtr = static_cast<sbIMediaListView*>(this);
1225 
1226  PRInt32 count = listeners.Count();
1227  for (PRInt32 index = 0; index < count; index++) {
1228  sbIMediaListViewListener* listener = listeners.ObjectAt(index);
1229  (listener->*aListenerFunc)(thisPtr);
1230  }
1231 }
1232 
1233 nsresult
1235 {
1236  NS_ENSURE_ARG_POINTER(aFilterConstraint);
1237 
1238  nsresult rv;
1239 
1240  if (mViewFilter) {
1241  nsCOMPtr<sbILibraryConstraintBuilder> builder =
1242  do_CreateInstance(SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID, &rv);
1243  NS_ENSURE_SUCCESS(rv, rv);
1244 
1245  rv = builder->IncludeConstraint(mViewFilter, nsnull);
1246  NS_ENSURE_SUCCESS(rv, rv);
1247 
1248  nsCOMPtr<sbILibraryConstraint> constraint;
1249  rv = builder->Get(getter_AddRefs(constraint));
1250  NS_ENSURE_SUCCESS(rv, rv);
1251 
1252  NS_ADDREF(*aFilterConstraint = constraint);
1253  }
1254  else {
1255  *aFilterConstraint = nsnull;
1256  }
1257 
1258  return NS_OK;
1259 }
1260 
1261 // sbIFilterableMediaListView
1262 NS_IMETHODIMP
1263 sbLocalDatabaseMediaListView::GetFilterConstraint(sbILibraryConstraint** aFilterConstraint)
1264 {
1265  NS_ENSURE_ARG_POINTER(aFilterConstraint);
1266 
1267  nsresult rv;
1268 
1269  nsCOMPtr<sbILibraryConstraintBuilder> builder =
1270  do_CreateInstance(SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID, &rv);
1271  NS_ENSURE_SUCCESS(rv, rv);
1272 
1273  if (mViewFilter) {
1274  rv = builder->IncludeConstraint(mViewFilter, nsnull);
1275  NS_ENSURE_SUCCESS(rv, rv);
1276  }
1277 
1278  // Add filters from the cascade filter list, if any
1279  PRBool changed = PR_FALSE;
1280  if (mCascadeFilterSet) {
1281  rv = mCascadeFilterSet->AddFilters(builder, &changed);
1282  NS_ENSURE_SUCCESS(rv, rv);
1283  }
1284 
1285  if (mViewFilter || changed) {
1286  nsCOMPtr<sbILibraryConstraint> constraint;
1287  rv = builder->Get(getter_AddRefs(constraint));
1288  NS_ENSURE_SUCCESS(rv, rv);
1289 
1290  NS_ADDREF(*aFilterConstraint = constraint);
1291  }
1292  else {
1293  *aFilterConstraint = nsnull;
1294  }
1295 
1296  return NS_OK;
1297 }
1298 
1299 NS_IMETHODIMP
1300 sbLocalDatabaseMediaListView::SetFilterConstraint(sbILibraryConstraint* aFilterConstraint)
1301 {
1302  nsresult rv;
1303 
1304  // We currently don't support filters with multiple properties in each group
1305  // This should go away when we support generalized library constraints for
1306  // filters (see bug 5955)
1307  if (aFilterConstraint) {
1308  PRUint32 groupCount;
1309  rv = aFilterConstraint->GetGroupCount(&groupCount);
1310  NS_ENSURE_SUCCESS(rv, rv);
1311 
1312  for (PRUint32 i = 0; i < groupCount; i++) {
1313  nsCOMPtr<sbILibraryConstraintGroup> group;
1314  rv = aFilterConstraint->GetGroup(i, getter_AddRefs(group));
1315  NS_ENSURE_SUCCESS(rv, rv);
1316 
1317  nsCOMPtr<nsIStringEnumerator> properties;
1318  rv = group->GetProperties(getter_AddRefs(properties));
1319  NS_ENSURE_SUCCESS(rv, rv);
1320 
1321  nsString junk;
1322  rv = properties->GetNext(junk);
1323  NS_ENSURE_SUCCESS(rv, rv);
1324 
1325  PRBool HasMore;
1326  rv = properties->HasMore(&HasMore);
1327  NS_ENSURE_SUCCESS(rv, rv);
1328 
1329  if (HasMore) {
1330  return NS_ERROR_INVALID_ARG;
1331  }
1332  }
1333  }
1334 
1335  mViewFilter = aFilterConstraint;
1336 
1337  // Clear filters from the cascade filter list, if any
1338  if (!aFilterConstraint && mCascadeFilterSet) {
1339  rv = mCascadeFilterSet->ClearFilters();
1340  NS_ENSURE_SUCCESS(rv, rv);
1341  }
1342 
1343  rv = UpdateViewArrayConfiguration(PR_TRUE);
1344  NS_ENSURE_SUCCESS(rv, rv);
1345 
1346  // And notify listeners
1348 
1349  return NS_OK;
1350 }
1351 
1352 // sbISearchableMediaListView
1353 NS_IMETHODIMP
1354 sbLocalDatabaseMediaListView::GetSearchConstraint(sbILibraryConstraint** aSearchConstraint)
1355 {
1356  NS_ENSURE_ARG_POINTER(aSearchConstraint);
1357 
1358  nsresult rv;
1359 
1360  nsCOMPtr<sbILibraryConstraintBuilder> builder =
1361  do_CreateInstance(SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID, &rv);
1362  NS_ENSURE_SUCCESS(rv, rv);
1363 
1364  if (mViewSearch) {
1365  rv = builder->IncludeConstraint(mViewSearch, nsnull);
1366  NS_ENSURE_SUCCESS(rv, rv);
1367  }
1368 
1369  // Add searches from the cascade filter list, if any
1370  PRBool changed = PR_FALSE;
1371  if (mCascadeFilterSet) {
1372  rv = mCascadeFilterSet->AddSearches(builder, &changed);
1373  NS_ENSURE_SUCCESS(rv, rv);
1374  }
1375 
1376  if (mViewSearch || changed) {
1377  nsCOMPtr<sbILibraryConstraint> constraint;
1378  rv = builder->Get(getter_AddRefs(constraint));
1379  NS_ENSURE_SUCCESS(rv, rv);
1380 
1381  NS_ADDREF(*aSearchConstraint = constraint);
1382  }
1383  else {
1384  *aSearchConstraint = nsnull;
1385  }
1386 
1387  return NS_OK;
1388 }
1389 
1390 NS_IMETHODIMP
1391 sbLocalDatabaseMediaListView::SetSearchConstraint(sbILibraryConstraint* aSearchConstraint)
1392 {
1393  nsresult rv;
1394 
1395  // Searches can only be specified as a constraint with a single group, and
1396  // only a single property within the group. This is due to the way the
1397  // sqlite fts works and it makes me wonder if we should make the parameter
1398  // to this method less general.
1399  if (aSearchConstraint) {
1400  PRUint32 groupCount;
1401  rv = aSearchConstraint->GetGroupCount(&groupCount);
1402  NS_ENSURE_SUCCESS(rv, rv);
1403 
1404  // Only one group allowed
1405  NS_ENSURE_TRUE(groupCount == 1, NS_ERROR_INVALID_ARG);
1406 
1407  nsCOMPtr<sbILibraryConstraintGroup> firstGroup;
1408  rv = aSearchConstraint->GetGroup(0, getter_AddRefs(firstGroup));
1409  NS_ENSURE_SUCCESS(rv, rv);
1410 
1411  nsCOMPtr<nsIStringEnumerator> firstGroupProperties;
1412  rv = firstGroup->GetProperties(getter_AddRefs(firstGroupProperties));
1413  NS_ENSURE_SUCCESS(rv, rv);
1414 
1415  nsString property;
1416  rv = firstGroupProperties->GetNext(property);
1417  NS_ENSURE_SUCCESS(rv, rv);
1418 
1419  // Only one property allowed
1420  PRBool hasMore;
1421  rv = firstGroupProperties->HasMore(&hasMore);
1422  NS_ENSURE_FALSE(hasMore, NS_ERROR_INVALID_ARG);
1423 
1424  // Make sure all of the values are not empty
1425  nsCOMPtr<nsIStringEnumerator> values;
1426  rv = firstGroup->GetValues(property, getter_AddRefs(values));
1427  NS_ENSURE_SUCCESS(rv, rv);
1428 
1429  PRBool hasMoreValues;
1430  while (NS_SUCCEEDED(values->HasMore(&hasMoreValues)) && hasMoreValues) {
1431  nsString value;
1432  rv = values->GetNext(value);
1433  NS_ENSURE_SUCCESS(rv, rv);
1434 
1435  if (value.IsEmpty()) {
1436  return NS_ERROR_INVALID_ARG;
1437  }
1438  }
1439  }
1440 
1441  mViewSearch = aSearchConstraint;
1442 
1443  // Clear searches from the cascade filter list, if any
1444  if (!aSearchConstraint && mCascadeFilterSet) {
1445  rv = mCascadeFilterSet->ClearSearches();
1446  NS_ENSURE_SUCCESS(rv, rv);
1447  }
1448 
1449  rv = UpdateViewArrayConfiguration(PR_TRUE);
1450  NS_ENSURE_SUCCESS(rv, rv);
1451 
1452  // And notify listeners
1454 
1455  return NS_OK;
1456 }
1457 
1458 // sbISortableMediaListView
1459 NS_IMETHODIMP
1460 sbLocalDatabaseMediaListView::GetSortableProperties(nsIStringEnumerator** aSortableProperties)
1461 {
1462  NS_ENSURE_ARG_POINTER(aSortableProperties);
1463  // To be implemented by property manager?
1464  return NS_ERROR_NOT_IMPLEMENTED;
1465 }
1466 
1467 NS_IMETHODIMP
1468 sbLocalDatabaseMediaListView::GetCurrentSort(sbIPropertyArray** aCurrentSort)
1469 {
1470  NS_ENSURE_ARG_POINTER(aCurrentSort);
1471  NS_ENSURE_STATE(mViewSort);
1472 
1473  NS_ADDREF(*aCurrentSort = mViewSort);
1474  return NS_OK;
1475 }
1476 
1477 nsresult
1478 sbLocalDatabaseMediaListView::SetSort(sbIPropertyArray* aSort)
1479 {
1480  nsresult rv = SetSortInternal(aSort);
1481  NS_ENSURE_SUCCESS(rv, rv);
1482 
1483  return rv;
1484 }
1485 
1486 nsresult
1487 sbLocalDatabaseMediaListView::SetSortInternal(sbIPropertyArray* aSort)
1488 {
1489  nsresult rv;
1490 
1491  if (aSort)
1492  {
1493  rv = ClonePropertyArray(aSort, getter_AddRefs(mViewSort));
1494  NS_ENSURE_SUCCESS(rv, rv);
1495  }
1496  else
1497  {
1498  mViewSort = do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1499  NS_ENSURE_SUCCESS(rv, rv);
1500 
1501  rv = mViewSort->SetStrict(PR_FALSE);
1502  NS_ENSURE_SUCCESS(rv, rv);
1503  }
1504 
1505  nsCOMPtr<nsIArray> array = do_QueryInterface(mViewSort, &rv);
1506  NS_ENSURE_SUCCESS(rv, rv);
1507 
1508  PRUint32 length;
1509  rv = array->GetLength(&length);
1510  NS_ENSURE_SUCCESS(rv, rv);
1511 
1512  if (!length)
1513  {
1514  rv = mViewSort->AppendProperty(mDefaultSortProperty,
1515  NS_LITERAL_STRING("a"));
1516  NS_ENSURE_SUCCESS(rv, rv);
1517  }
1518 
1519  rv = UpdateViewArrayConfiguration(PR_FALSE);
1520  NS_ENSURE_SUCCESS(rv, rv);
1521 
1522  rv = UpdateListener(PR_FALSE);
1523  NS_ENSURE_SUCCESS(rv, rv);
1524 
1525  // And notify listeners
1527 
1528  return NS_OK;
1529 }
1530 
1531 NS_IMETHODIMP
1532 sbLocalDatabaseMediaListView::ClearSort()
1533 {
1534  nsresult rv;
1535 
1536  if (mViewSort) {
1537  nsCOMPtr<nsIMutableArray> array = do_QueryInterface(mViewSort, &rv);
1538  NS_ENSURE_SUCCESS(rv, rv);
1539 
1540  rv = array->Clear();
1541  NS_ENSURE_SUCCESS(rv, rv);
1542 
1543  rv = SetSort(nsnull);
1544  NS_ENSURE_SUCCESS(rv, rv);
1545  }
1546 
1547  // And notify listeners
1549 
1550  return NS_OK;
1551 }
1552 
1553 // sbIMediaListListener
1554 NS_IMETHODIMP
1555 sbLocalDatabaseMediaListView::OnItemAdded(sbIMediaList* aMediaList,
1556  sbIMediaItem* aMediaItem,
1557  PRUint32 aIndex,
1558  PRBool* aNoMoreForBatch)
1559 {
1560  NS_ENSURE_ARG_POINTER(aMediaList);
1561  NS_ENSURE_ARG_POINTER(aMediaItem);
1562  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1563 
1564  if (mBatchHelper.IsActive()) {
1565  mInvalidatePending = PR_TRUE;
1566  *aNoMoreForBatch = PR_TRUE;
1567  return NS_OK;
1568  }
1569 
1570  // Invalidate the view array. Adding an item definitely invalidates length.
1571  nsresult rv = Invalidate(PR_TRUE);
1572  NS_ENSURE_SUCCESS(rv, rv);
1573 
1574  *aNoMoreForBatch = PR_FALSE;
1575  return NS_OK;
1576 }
1577 
1578 NS_IMETHODIMP
1579 sbLocalDatabaseMediaListView::OnBeforeItemRemoved(sbIMediaList* aMediaList,
1580  sbIMediaItem* aMediaItem,
1581  PRUint32 aIndex,
1582  PRBool* aNoMoreForBatch)
1583 {
1584  NS_ENSURE_ARG_POINTER(aMediaList);
1585  NS_ENSURE_ARG_POINTER(aMediaItem);
1586  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1587 
1588  // Don't care
1589 
1590  *aNoMoreForBatch = PR_TRUE;
1591  return NS_OK;
1592 }
1593 
1594 NS_IMETHODIMP
1595 sbLocalDatabaseMediaListView::OnAfterItemRemoved(sbIMediaList* aMediaList,
1596  sbIMediaItem* aMediaItem,
1597  PRUint32 aIndex,
1598  PRBool* aNoMoreForBatch)
1599 {
1600  NS_ENSURE_ARG_POINTER(aMediaList);
1601  NS_ENSURE_ARG_POINTER(aMediaItem);
1602  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1603 
1604  if (mBatchHelper.IsActive()) {
1605  mInvalidatePending = PR_TRUE;
1606  *aNoMoreForBatch = PR_TRUE;
1607  return NS_OK;
1608  }
1609 
1610  // Invalidate the view array. Removing items invalidates length.
1611  nsresult rv = Invalidate(PR_TRUE);
1612  NS_ENSURE_SUCCESS(rv, rv);
1613 
1614  *aNoMoreForBatch = PR_FALSE;
1615  return NS_OK;
1616 }
1617 
1618 NS_IMETHODIMP
1619 sbLocalDatabaseMediaListView::OnItemUpdated(sbIMediaList* aMediaList,
1620  sbIMediaItem* aMediaItem,
1621  sbIPropertyArray* aProperties,
1622  PRBool* aNoMoreForBatch)
1623 {
1624  NS_ENSURE_ARG_POINTER(aMediaList);
1625  NS_ENSURE_ARG_POINTER(aMediaItem);
1626  NS_ENSURE_ARG_POINTER(aProperties);
1627  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1628 
1629 #ifdef PR_LOGGING
1630  nsAutoString buff;
1631  aProperties->ToString(buff);
1632  TRACE(("sbLocalDatabaseMediaListView[0x%.8x] - OnItemUpdated %s",
1633  this, NS_ConvertUTF16toUTF8(buff).get()));
1634 #endif
1635 
1636  nsresult rv;
1637 
1638  // If we are in a batch, we don't need any more notifications since we always
1639  // invalidate when a batch ends
1640  PRBool shouldInvalidate;
1641  if (mBatchHelper.IsActive()) {
1642  shouldInvalidate = PR_FALSE;
1643  mInvalidatePending = PR_TRUE;
1644  *aNoMoreForBatch = PR_TRUE;
1645  }
1646  else {
1647  // If we are not in a batch, check to see if this update should cause an
1648  // invalidation
1649  rv = ShouldCauseInvalidation(aProperties, &shouldInvalidate);
1650  NS_ENSURE_SUCCESS(rv, rv);
1651  *aNoMoreForBatch = PR_FALSE;
1652  }
1653 
1654  if (shouldInvalidate) {
1655  // Invalidate the view array. Properties changed significantly.
1656  // We need to invalidate length as well in this case.
1657  nsresult rv = Invalidate(PR_TRUE);
1658  NS_ENSURE_SUCCESS(rv, rv);
1659  }
1660  else {
1661  // If the array has not already been invalidated, we should invalidate the
1662  // row of the tree view that contains this item. This lets us see updates
1663  // that don't cause invalidations
1664  if (mTreeView) {
1665  nsAutoString guid;
1666  rv = aMediaItem->GetGuid(guid);
1667  NS_ENSURE_SUCCESS(rv, rv);
1668 
1669  rv = mTreeView->InvalidateRowsByGuid(guid);
1670  NS_ENSURE_SUCCESS(rv, rv);
1671  }
1672  }
1673 
1674  return NS_OK;
1675 }
1676 
1677 NS_IMETHODIMP
1678 sbLocalDatabaseMediaListView::OnItemMoved(sbIMediaList* aMediaList,
1679  PRUint32 aFromIndex,
1680  PRUint32 aToIndex,
1681  PRBool* aNoMoreForBatch)
1682 {
1683  NS_ENSURE_ARG_POINTER(aMediaList);
1684  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1685 
1686  if (mBatchHelper.IsActive()) {
1687  mInvalidatePending = PR_TRUE;
1688  *aNoMoreForBatch = PR_TRUE;
1689  return NS_OK;
1690  }
1691 
1692  // Invalidate the view array. Moving doesn't invalidate length.
1693  nsresult rv = Invalidate(PR_FALSE);
1694  NS_ENSURE_SUCCESS(rv, rv);
1695 
1696  *aNoMoreForBatch = PR_FALSE;
1697  return NS_OK;
1698 }
1699 
1700 NS_IMETHODIMP
1701 sbLocalDatabaseMediaListView::OnBeforeListCleared(sbIMediaList* aMediaList,
1702  PRBool aExcludeLists,
1703  PRBool* aNoMoreForBatch)
1704 {
1705  NS_ENSURE_ARG_POINTER(aMediaList);
1706  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1707 
1708  // Don't care
1709 
1710  *aNoMoreForBatch = PR_TRUE;
1711  return NS_OK;
1712 }
1713 
1714 NS_IMETHODIMP
1715 sbLocalDatabaseMediaListView::OnListCleared(sbIMediaList* aMediaList,
1716  PRBool aExcludeLists,
1717  PRBool* aNoMoreForBatch)
1718 {
1719  NS_ENSURE_ARG_POINTER(aMediaList);
1720  NS_ENSURE_ARG_POINTER(aNoMoreForBatch);
1721 
1722  if (mBatchHelper.IsActive()) {
1723  mInvalidatePending = PR_TRUE;
1724  *aNoMoreForBatch = PR_TRUE;
1725  return NS_OK;
1726  }
1727 
1728  // Invalidate the view array. Clearing totally invalidates length.
1729  nsresult rv = Invalidate(PR_TRUE);
1730  NS_ENSURE_SUCCESS(rv, rv);
1731 
1732  *aNoMoreForBatch = PR_FALSE;
1733  return NS_OK;
1734 }
1735 
1736 NS_IMETHODIMP
1737 sbLocalDatabaseMediaListView::OnBatchBegin(sbIMediaList* aMediaList)
1738 {
1739  mBatchHelper.Begin();
1740 
1741  if (mTreeView) {
1742  mTreeView->SetShouldPreventRebuild(PR_TRUE);
1743  }
1744 
1745  return NS_OK;
1746 }
1747 
1748 NS_IMETHODIMP
1749 sbLocalDatabaseMediaListView::OnBatchEnd(sbIMediaList* aMediaList)
1750 {
1751  mBatchHelper.End();
1752 
1753  if (!mBatchHelper.IsActive()) {
1754  if (mInvalidatePending) {
1755  // Invalidate the view array. No way to tell accurately in batches
1756  // if the length is invalidated or not so we always invalidate.
1757  nsresult rv = Invalidate(PR_TRUE);
1758  NS_ENSURE_SUCCESS(rv, rv);
1759 
1760  mInvalidatePending = PR_FALSE;
1761  }
1762 
1763  // We have to rebuild the tree manually here because there is
1764  // no guarantee that the tree will be invalidated at the correct
1765  // time. If the tree is invalidated after the view invalidates
1766  // it will _never_ get its callback telling it to rebuild :(
1767  if (mTreeView) {
1768  mTreeView->SetShouldPreventRebuild(PR_FALSE);
1769  mTreeView->Rebuild();
1770  }
1771  }
1772 
1773  return NS_OK;
1774 }
1775 
1776 nsresult
1778 {
1779  nsresult rv;
1780 
1781  sbSuppressArrayInvalidationView suppressInvaliation(mArray, mSelection);
1782 
1783  rv = mArray->ClearFilters();
1784  NS_ENSURE_SUCCESS(rv, rv);
1785 
1786  // Update filters
1787  // XXXsteve Eventually we should simply pass the library constraint directly
1788  // to the guid array
1789  if (mViewFilter) {
1790  PRUint32 groupCount;
1791  rv = mViewFilter->GetGroupCount(&groupCount);
1792  NS_ENSURE_SUCCESS(rv, rv);
1793 
1794  for (PRUint32 i = 0; i < groupCount; i++) {
1795 
1796  nsCOMPtr<sbILibraryConstraintGroup> group;
1797  rv = mViewFilter->GetGroup(i, getter_AddRefs(group));
1798  NS_ENSURE_SUCCESS(rv, rv);
1799 
1800  nsCOMPtr<nsIStringEnumerator> properties;
1801  rv = group->GetProperties(getter_AddRefs(properties));
1802  NS_ENSURE_SUCCESS(rv, rv);
1803 
1804  // XXXsteve We only support one property in a group
1805  nsString property;
1806  rv = properties->GetNext(property);
1807  NS_ENSURE_SUCCESS(rv, rv);
1808 
1809  nsCOMPtr<nsIStringEnumerator> values;
1810  rv = group->GetValues(property, getter_AddRefs(values));
1811  NS_ENSURE_SUCCESS(rv, rv);
1812 
1813  // Top level properties are not filtered as sortable
1814  if (!SB_IsTopLevelProperty(property)) {
1815 
1816  nsCOMPtr<sbIPropertyInfo> info;
1817  rv = mPropMan->GetPropertyInfo(property, getter_AddRefs(info));
1818  NS_ENSURE_SUCCESS(rv, rv);
1819 
1820  nsCOMPtr<nsIStringEnumerator> sortable =
1821  new sbMakeSortableStringEnumerator(info, values);
1822  NS_ENSURE_TRUE(sortable, NS_ERROR_OUT_OF_MEMORY);
1823 
1824  values = sortable;
1825  }
1826 
1827  // Set the filter.
1828  rv = mArray->AddFilter(property, values, PR_FALSE);
1829  NS_ENSURE_SUCCESS(rv, rv);
1830  }
1831  }
1832 
1833  // Update searches
1834  // XXXsteve Eventually we should simply pass the library constraint directly
1835  // to the guid array
1836  if (mViewSearch) {
1837  PRUint32 groupCount;
1838  rv = mViewSearch->GetGroupCount(&groupCount);
1839  NS_ENSURE_SUCCESS(rv, rv);
1840 
1841  // Since we know each group has the same properies, just use the properties
1842  // from the first group to assemble the data
1843  nsCOMPtr<sbILibraryConstraintGroup> firstGroup;
1844  rv = mViewSearch->GetGroup(0, getter_AddRefs(firstGroup));
1845  NS_ENSURE_SUCCESS(rv, rv);
1846 
1847  nsCOMPtr<nsIStringEnumerator> firstGroupProperties;
1848  rv = firstGroup->GetProperties(getter_AddRefs(firstGroupProperties));
1849  NS_ENSURE_SUCCESS(rv, rv);
1850 
1851  PRBool hasMore;
1852  while (NS_SUCCEEDED(firstGroupProperties->HasMore(&hasMore)) && hasMore) {
1853  nsString property;
1854  rv = firstGroupProperties->GetNext(property);
1855  NS_ENSURE_SUCCESS(rv, rv);
1856 
1857  // Collect the values for this property from each group
1858  sbStringArray allValues;
1859  for (PRUint32 i = 0; i < groupCount; i++) {
1860  nsCOMPtr<sbILibraryConstraintGroup> group;
1861  rv = mViewSearch->GetGroup(i, getter_AddRefs(group));
1862  NS_ENSURE_SUCCESS(rv, rv);
1863 
1864  nsCOMPtr<nsIStringEnumerator> values;
1865  rv = group->GetValues(property, getter_AddRefs(values));
1866  NS_ENSURE_SUCCESS(rv, rv);
1867 
1868  PRBool hasMoreValues;
1869  while (NS_SUCCEEDED(values->HasMore(&hasMoreValues)) && hasMoreValues) {
1870  nsString value;
1871  rv = values->GetNext(value);
1872  NS_ENSURE_SUCCESS(rv, rv);
1873 
1874  nsString* added = allValues.AppendElement(value);
1875  NS_ENSURE_TRUE(added, NS_ERROR_OUT_OF_MEMORY);
1876  }
1877 
1878  nsCOMPtr<nsIStringEnumerator> allValuesEnum =
1879  new sbTArrayStringEnumerator(&allValues);
1880  NS_ENSURE_TRUE(allValuesEnum, NS_ERROR_OUT_OF_MEMORY);
1881 
1882  // Top level properties are not filtered as sortable
1883  if (!SB_IsTopLevelProperty(property)) {
1884 
1885  nsCOMPtr<sbIPropertyInfo> info;
1886  rv = mPropMan->GetPropertyInfo(property, getter_AddRefs(info));
1887  NS_ENSURE_SUCCESS(rv, rv);
1888 
1889  nsCOMPtr<nsIStringEnumerator> sortable =
1890  new sbMakeSortableStringEnumerator(info, allValuesEnum);
1891  NS_ENSURE_TRUE(sortable, NS_ERROR_OUT_OF_MEMORY);
1892 
1893  allValuesEnum = sortable;
1894  }
1895 
1896  rv = mArray->AddFilter(property, allValuesEnum, PR_TRUE);
1897  NS_ENSURE_SUCCESS(rv, rv);
1898  }
1899  }
1900  }
1901 
1902  // Add configuration from the cascade filter list, if any
1903  if (mCascadeFilterSet) {
1904  rv = mCascadeFilterSet->AddConfiguration(mArray);
1905  NS_ENSURE_SUCCESS(rv, rv);
1906  }
1907 
1908  // Update sort
1909  rv = mArray->ClearSorts();
1910  NS_ENSURE_SUCCESS(rv, rv);
1911 
1912  PRBool hasSorts = PR_FALSE;
1913  if (mViewSort) {
1914  PRUint32 propertyCount;
1915  rv = mViewSort->GetLength(&propertyCount);
1916  NS_ENSURE_SUCCESS(rv, rv);
1917 
1918  for (PRUint32 index = 0; index < propertyCount; index++) {
1919 
1920  nsCOMPtr<sbIProperty> property;
1921  rv = mViewSort->GetPropertyAt(index, getter_AddRefs(property));
1922  NS_ENSURE_SUCCESS(rv, rv);
1923 
1924  nsString propertyID;
1925  rv = property->GetId(propertyID);
1926  NS_ENSURE_SUCCESS(rv, rv);
1927 
1928  nsString value;
1929  rv = property->GetValue(value);
1930  NS_ENSURE_SUCCESS(rv, rv);
1931 
1932  mArray->AddSort(propertyID, value.EqualsLiteral("a"));
1933  NS_ENSURE_SUCCESS(rv, rv);
1934 
1935  hasSorts = PR_TRUE;
1936  }
1937  }
1938 
1939  // If no sort is specified, use the default sort
1940  if (!hasSorts) {
1941  mArray->AddSort(mDefaultSortProperty, PR_TRUE);
1942  NS_ENSURE_SUCCESS(rv, rv);
1943  }
1944 
1945  // Clear selection if requested
1946  if (aClearTreeSelection) {
1947  rv = mSelection->SelectNone();
1948  NS_ENSURE_SUCCESS(rv, rv);
1949  }
1950 
1951  return NS_OK;
1952 }
1953 
1954 nsresult
1955 sbLocalDatabaseMediaListView::MakeStandardQuery(sbIDatabaseQuery** _retval)
1956 {
1957  nsresult rv;
1958  nsCOMPtr<sbIDatabaseQuery> query =
1959  do_CreateInstance(SONGBIRD_DATABASEQUERY_CONTRACTID, &rv);
1960  NS_ENSURE_SUCCESS(rv, rv);
1961 
1962  nsAutoString databaseGuid;
1963  rv = mLibrary->GetDatabaseGuid(databaseGuid);
1964  NS_ENSURE_SUCCESS(rv, rv);
1965 
1966  rv = query->SetDatabaseGUID(databaseGuid);
1967  NS_ENSURE_SUCCESS(rv, rv);
1968 
1969  nsCOMPtr<nsIURI> databaseLocation;
1970  rv = mLibrary->GetDatabaseLocation(getter_AddRefs(databaseLocation));
1971  NS_ENSURE_SUCCESS(rv, rv);
1972 
1973  if (databaseLocation) {
1974  rv = query->SetDatabaseLocation(databaseLocation);
1975  NS_ENSURE_SUCCESS(rv, rv);
1976  }
1977 
1978  rv = query->SetAsyncQuery(PR_FALSE);
1979  NS_ENSURE_SUCCESS(rv, rv);
1980 
1981  NS_ADDREF(*_retval = query);
1982  return NS_OK;
1983 }
1984 
1985 nsresult
1986 sbLocalDatabaseMediaListView::CreateQueries()
1987 {
1988  nsresult rv;
1989 
1990  nsCOMPtr<sbISQLSelectBuilder> builder =
1991  do_CreateInstance(SB_SQLBUILDER_SELECT_CONTRACTID, &rv);
1992  NS_ENSURE_SUCCESS(rv, rv);
1993 
1994  nsCOMPtr<sbISQLBuilderCriterion> criterion;
1995 
1996  // Create distinct property values query
1997  rv = builder->SetDistinct(PR_TRUE);
1998  NS_ENSURE_SUCCESS(rv, rv);
1999 
2000  rv = builder->AddColumn(NS_LITERAL_STRING("_rp"),
2001  NS_LITERAL_STRING("obj"));
2002  NS_ENSURE_SUCCESS(rv, rv);
2003 
2004  if (mMediaListId == 0) {
2005  rv = builder->SetBaseTableName(NS_LITERAL_STRING("properties"));
2006  NS_ENSURE_SUCCESS(rv, rv);
2007 
2008  rv = builder->SetBaseTableAlias(NS_LITERAL_STRING("_p"));
2009  NS_ENSURE_SUCCESS(rv, rv);
2010 
2011  rv = builder->SetDistinct(PR_TRUE);
2012  NS_ENSURE_SUCCESS(rv, rv);
2013 
2014  rv = builder->AddColumn(NS_LITERAL_STRING("_rp"),
2015  NS_LITERAL_STRING("obj"));
2016  NS_ENSURE_SUCCESS(rv, rv);
2017 
2018  rv = builder->AddJoin(sbISQLSelectBuilder::JOIN_INNER,
2019  NS_LITERAL_STRING("resource_properties"),
2020  NS_LITERAL_STRING("_rp"),
2021  NS_LITERAL_STRING("property_id"),
2022  NS_LITERAL_STRING("_p"),
2023  NS_LITERAL_STRING("property_id"));
2024  NS_ENSURE_SUCCESS(rv, rv);
2025 
2026  rv = builder->CreateMatchCriterionParameter(NS_LITERAL_STRING("_p"),
2027  NS_LITERAL_STRING("property_name"),
2029  getter_AddRefs(criterion));
2030  NS_ENSURE_SUCCESS(rv, rv);
2031 
2032  rv = builder->AddCriterion(criterion);
2033  NS_ENSURE_SUCCESS(rv, rv);
2034  }
2035  else {
2036  rv = builder->SetBaseTableName(NS_LITERAL_STRING("media_items"));
2037  NS_ENSURE_SUCCESS(rv, rv);
2038 
2039  rv = builder->SetBaseTableAlias(NS_LITERAL_STRING("_mi"));
2040  NS_ENSURE_SUCCESS(rv, rv);
2041 
2042  rv = builder->AddJoin(sbISQLSelectBuilder::JOIN_INNER,
2043  NS_LITERAL_STRING("simple_media_lists"),
2044  NS_LITERAL_STRING("_sml"),
2045  NS_LITERAL_STRING("member_media_item_id"),
2046  NS_LITERAL_STRING("_mi"),
2047  NS_LITERAL_STRING("media_item_id"));
2048  NS_ENSURE_SUCCESS(rv, rv);
2049 
2050  rv = builder->CreateMatchCriterionLong(NS_LITERAL_STRING("_sml"),
2051  NS_LITERAL_STRING("media_item_id"),
2053  mMediaListId,
2054  getter_AddRefs(criterion));
2055  NS_ENSURE_SUCCESS(rv, rv);
2056 
2057  rv = builder->AddCriterion(criterion);
2058  NS_ENSURE_SUCCESS(rv, rv);
2059 
2060  rv = builder->AddJoin(sbISQLSelectBuilder::JOIN_INNER,
2061  NS_LITERAL_STRING("resource_properties"),
2062  NS_LITERAL_STRING("_rp"),
2063  NS_LITERAL_STRING("guid"),
2064  NS_LITERAL_STRING("_mi"),
2065  NS_LITERAL_STRING("guid"));
2066  NS_ENSURE_SUCCESS(rv, rv);
2067 
2068  rv = builder->AddJoin(sbISQLSelectBuilder::JOIN_INNER,
2069  NS_LITERAL_STRING("properties"),
2070  NS_LITERAL_STRING("_p"),
2071  NS_LITERAL_STRING("property_id"),
2072  NS_LITERAL_STRING("_rp"),
2073  NS_LITERAL_STRING("property_id"));
2074  NS_ENSURE_SUCCESS(rv, rv);
2075 
2076  rv = builder->CreateMatchCriterionParameter(NS_LITERAL_STRING("_p"),
2077  NS_LITERAL_STRING("property_name"),
2079  getter_AddRefs(criterion));
2080  NS_ENSURE_SUCCESS(rv, rv);
2081 
2082  rv = builder->AddCriterion(criterion);
2083  NS_ENSURE_SUCCESS(rv, rv);
2084  }
2085 
2086  rv = builder->AddOrder(NS_LITERAL_STRING("_rp"),
2087  NS_LITERAL_STRING("obj_sortable"),
2088  PR_TRUE);
2089  NS_ENSURE_SUCCESS(rv, rv);
2090 
2091  rv = builder->ToString(mDistinctPropertyValuesQuery);
2092  NS_ENSURE_SUCCESS(rv, rv);
2093 
2094  return NS_OK;
2095 }
2096 
2097 nsresult
2098 sbLocalDatabaseMediaListView::Invalidate(PRBool aInvalidateLength)
2099 {
2100  LOG(("sbLocalDatabaseMediaListView[0x%.8x] - Invalidate", this));
2101  nsresult rv;
2102 
2103  // Invalidate the view array.
2104  rv = mArray->Invalidate(aInvalidateLength);
2105  NS_ENSURE_SUCCESS(rv, rv);
2106 
2107  // Notify our selection that things have changed
2108  rv = mSelection->ConfigurationChanged();
2109  NS_ENSURE_SUCCESS(rv, rv);
2110 
2111  return NS_OK;
2112 }
2113 
2114 // nsIClassInfo
2115 NS_IMETHODIMP
2116 sbLocalDatabaseMediaListView::GetInterfaces(PRUint32* count, nsIID*** array)
2117 {
2118  return NS_CI_INTERFACE_GETTER_NAME(sbLocalDatabaseMediaListView)(count, array);
2119 }
2120 
2121 NS_IMETHODIMP
2122 sbLocalDatabaseMediaListView::GetHelperForLanguage(PRUint32 language,
2123  nsISupports** _retval)
2124 {
2125  *_retval = nsnull;
2126  return NS_OK;
2127 }
2128 
2129 NS_IMETHODIMP
2130 sbLocalDatabaseMediaListView::GetContractID(char** aContractID)
2131 {
2132  *aContractID = nsnull;
2133  return NS_OK;
2134 }
2135 
2136 NS_IMETHODIMP
2137 sbLocalDatabaseMediaListView::GetClassDescription(char** aClassDescription)
2138 {
2139  *aClassDescription = nsnull;
2140  return NS_OK;
2141 }
2142 
2143 NS_IMETHODIMP
2144 sbLocalDatabaseMediaListView::GetClassID(nsCID** aClassID)
2145 {
2146  *aClassID = nsnull;
2147  return NS_OK;
2148 }
2149 
2150 NS_IMETHODIMP
2151 sbLocalDatabaseMediaListView::GetImplementationLanguage(PRUint32* aImplementationLanguage)
2152 {
2153  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
2154  return NS_OK;
2155 }
2156 
2157 NS_IMETHODIMP
2158 sbLocalDatabaseMediaListView::GetFlags(PRUint32 *aFlags)
2159 {
2160  *aFlags = 0;
2161  return NS_OK;
2162 }
2163 
2164 NS_IMETHODIMP
2165 sbLocalDatabaseMediaListView::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
2166 {
2167  return NS_ERROR_NOT_AVAILABLE;
2168 }
2169 
2171 
2173  nsIStringEnumerator* aValues) :
2174  mPropertyInfo(aPropertyInfo),
2175  mValues(aValues)
2176 {
2177  NS_ASSERTION(aPropertyInfo, "aPropertyInfo is null");
2178  NS_ASSERTION(aValues, "aValues is null");
2179 }
2180 
2181 NS_IMETHODIMP
2182 sbMakeSortableStringEnumerator::HasMore(PRBool* _retval)
2183 {
2184  return mValues->HasMore(_retval);
2185 }
2186 
2187 NS_IMETHODIMP
2188 sbMakeSortableStringEnumerator::GetNext(nsAString& _retval)
2189 {
2190  nsresult rv;
2191 
2192  nsString value;
2193  rv = mValues->GetNext(value);
2194  NS_ENSURE_SUCCESS(rv, rv);
2195 
2196  rv = mPropertyInfo->MakeSortable(value, _retval);
2197  NS_ENSURE_SUCCESS(rv, rv);
2198 
2199  return NS_OK;
2200 }
2201 
classDescription entry
Definition: FeedWriter.js:1427
sbILocalDatabaseGUIDArray * GetGUIDArray()
sbIMediaListView clone()
Create a clone of this view. This will return a copy with the same search, filter, sort, and cascade filter set as the original.
return NS_OK
#define SB_PROPERTY_PLAYCOUNT
#define SONGBIRD_DATABASEQUERY_CONTRACTID
Definition: DatabaseQuery.h:63
Manage the selection of items within a view. This interface is a subset of nsITreeViewSelection. Note that if you are part of the user interface, you probably want to be calling nsITreeView.selection instead - otherwise things can get subtly out of sync.
NS_IMPL_ISUPPORTS1(sbDeviceCapabilitiesUtils, sbIDeviceCapabilitiesUtils) sbDeviceCapabilitiesUtils
#define LOG(args)
nsTArray< nsString > sbStringArray
inArray array
Cascade filter management for a media list.
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
nsresult UpdateViewArrayConfiguration(PRBool aClearTreeSelection)
#define SB_PROPERTY_HIDDEN
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
#define SB_LOCALDATABASE_ASYNCGUIDARRAY_CONTRACTID
static PRBool SB_IsTopLevelProperty(PRUint32 aPropertyDBID)
#define SB_PROPERTY_LASTPLAYTIME
var language
Definition: Info.js:44
A brief description of the contents of this interface.
#define SONGBIRD_LIBRARY_CONSTRAINTBUILDER_CONTRACTID
Definition: sbLibraryCID.h:40
A distinct view on a given media list.
#define SB_PROPERTYMANAGER_CONTRACTID
Interface used to listen to changes to a media list.
An interface to carry around arrays of nsIProperty instances Note that implementations of the interfa...
nsresult Init(sbIMediaListViewState *aState)
Control the search settings on a media list.
Saved state of a media list view.
#define SB_SQLBUILDER_SELECT_CONTRACTID
var count
Definition: test_bug7406.js:32
NS_DECL_ISUPPORTS NS_DECL_SBIMEDIALISTVIEW NS_DECL_SBIMEDIALISTLISTENER NS_DECL_SBIFILTERABLEMEDIALISTVIEW NS_DECL_SBISEARCHABLEMEDIALISTVIEW NS_DECL_SBISORTABLEMEDIALISTVIEW NS_DECL_NSICLASSINFO sbLocalDatabaseMediaListView(sbLocalDatabaseLibrary *aLibrary, sbLocalDatabaseMediaListBase *aMediaList, nsAString &aDefaultSortProperty, PRUint32 aMediaListId)
NS_IMPL_ISUPPORTS7(sbLocalDatabaseMediaListView, sbIMediaListView, sbIMediaListListener, sbIFilterableMediaListView, sbISearchableMediaListView, sbISortableMediaListView, nsIClassInfo, nsISupportsWeakReference) NS_IMPL_CI_INTERFACE_GETTER7(sbLocalDatabaseMediaListView
function search(aFolderId, aSearchStr, aExpectedScopeButtonId)
#define SB_PROPERTY_CONTENTTYPE
Control the filter settings on a media list.
An interface used to describe a metadata property for use by the UI and other sbILibrary interfaces (...
const unsigned long JOIN_INNER
nsresult GetViewConstraint(sbILibraryConstraint **aFilterConstraint)
Saved state of a media list view.
#define TRACE(args)
NS_IMETHOD GetSelection(sbLocalDatabaseMediaListViewSelectionState **aState)
StringArrayEnumerator prototype hasMore
countRef value
Definition: FeedWriter.js:1423
sbSuppressArrayInvalidationView(sbILocalDatabaseGUIDArray *aArray, sbLocalDatabaseMediaListViewSelection *aSelection)
static void AppendInt(nsAString &str, PRInt64 val)
already_AddRefed< sbLocalDatabaseMediaListBase > GetNativeMediaList()
An object responsible for executing SQL queries on the database.
#define SB_PROPERTY_LASTSKIPTIME
const unsigned long LISTENER_FLAGS_ALL
Interface that defines a single item of media in the system.
Control the sort settings on a media list.
readonly attribute sbIMediaListViewSelection selection
Get the selection for the view.
readonly attribute unsigned long length
Returns the length of filtered view of this list.
#define SB_PROPERTY_ISLIST
restoreWindow aState
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
#define DEFAULT_FETCH_SIZE
_getSelectedPageStyle s i
Array filter(tab.attributes, function(aAttr){return(_this.xulAttributes.indexOf(aAttr.name) >-1);}).forEach(tab.removeAttribute
#define SB_PROPERTY_SKIPCOUNT
var group
const unsigned long MATCH_EQUALS
Songbird Database Object Definition.