sbLocalDatabaseTreeView.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-2011 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 
29 #include "sbFilterTreeSelection.h"
30 
31 #include <nsIAtom.h>
32 #include <nsIAtomService.h>
33 #include <nsIClassInfoImpl.h>
34 #include <nsIDOMElement.h>
35 #include <nsIObjectOutputStream.h>
36 #include <nsIObjectInputStream.h>
37 #include <nsIObserverService.h>
38 #include <nsIProgrammingLanguage.h>
39 #include <nsIStringBundle.h>
40 #include <nsIStringEnumerator.h>
41 #include <nsITreeBoxObject.h>
42 #include <nsITreeColumns.h>
43 #include <nsIVariant.h>
44 #include <sbIClickablePropertyInfo.h>
45 #include <sbILocalDatabaseLibrary.h>
46 #include <sbILocalDatabasePropertyCache.h>
47 #include <sbILibrary.h>
48 #include <sbILibraryConstraints.h>
49 #include <sbIDevice.h>
50 #include <sbIDeviceManager.h>
51 #include <sbIMediacoreEvent.h>
52 #include <sbIMediacoreEventTarget.h>
53 #include <sbIMediacoreManager.h>
54 #include <sbIMediacoreSequencer.h>
55 #include <sbIMediacoreStatus.h>
56 #include <sbIMediaListView.h>
57 #include <sbIMediaList.h>
58 #include <sbIMediaItem.h>
59 #include <sbIMediaItemController.h>
60 #include <sbIPropertyArray.h>
61 #include <sbIPropertyInfo.h>
62 #include <sbIPropertyManager.h>
63 #include <sbIPropertyUnitConverter.h>
64 #include <sbISortableMediaListView.h>
65 #include <sbITreeViewPropertyInfo.h>
66 
67 #include <nsComponentManagerUtils.h>
68 #include <nsMemory.h>
69 #include <nsServiceManagerUtils.h>
70 #include <nsThreadUtils.h>
71 #include <nsUnicharUtils.h>
72 #include <nsCoord.h>
73 #include <prlog.h>
75 #include "sbLocalDatabaseCID.h"
79 #include <sbLibraryCID.h>
80 #include <sbPropertiesCID.h>
81 #include <sbStandardProperties.h>
82 #include <sbStringUtils.h>
84 
85 #ifdef DEBUG
86 #include <prprf.h>
87 #endif
88 
89 /*
90  * To log this module, set the following environment variable:
91  * NSPR_LOG_MODULES=sbLocalDatabaseTreeView:5
92  */
93 #ifdef PR_LOGGING
94 static PRLogModuleInfo* gLocalDatabaseTreeViewLog = nsnull;
95 #define TRACE(args) PR_LOG(gLocalDatabaseTreeViewLog, PR_LOG_DEBUG, args)
96 #define LOG(args) PR_LOG(gLocalDatabaseTreeViewLog, PR_LOG_WARN, args)
97 #else
98 #define TRACE(args) /* nothing */
99 #define LOG(args) /* nothing */
100 #endif
101 
102 #define PROGRESS_VALUE_UNSET -1
103 #define PROGRESS_VALUE_COMPLETE 101
104 
105 #define SB_STRING_BUNDLE_CHROME_URL "chrome://songbird/locale/songbird.properties"
106 
107 #define BAD_CSS_CHARS "/.:# !@$%^&*(),?;'\"<>~=+`\\|[]{}"
108 
109 /*
110  * There are two distinct coordinate systems used in this file, one for the
111  * underlying GUID array and one for the rows of the tree. The term "index"
112  * always refers to an index into the guid array, and the term "length" refers
113  * to the length of the guid array. The term "row" always refers to a row
114  * in the tree, and the term "row count" refers to the total number of rows
115  * in the tree.
116  *
117  * The TreeToArray and ArrayToTree methods are used to convert between the
118  * two coordinate systems.
119  */
120 
122 {
123 public:
124  sbAutoUpdateBatch(nsITreeBoxObject* aTreeBoxObject) :
125  mTreeBoxObject(aTreeBoxObject)
126  {
127  NS_ASSERTION(aTreeBoxObject, "aTreeBoxObject is null");
128 #ifdef DEBUG
129  nsresult rv =
130 #endif
131  mTreeBoxObject->BeginUpdateBatch();
132  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to begin");
133  }
134 
136  {
137 #ifdef DEBUG
138  nsresult rv =
139 #endif
140  mTreeBoxObject->EndUpdateBatch();
141  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to end");
142  }
143 
144 private:
145  nsITreeBoxObject* mTreeBoxObject;
146 };
147 
149 {
150 public:
151  explicit
153  : mArray(aArray) {
154  mArray->SuppressInvalidation(PR_TRUE);
155  }
156 
158  mArray->SuppressInvalidation(PR_FALSE);
159  }
160 private:
161  nsCOMPtr<sbILocalDatabaseGUIDArray> mArray;
162 };
163 
164 /* static */ nsresult PR_CALLBACK
165 sbLocalDatabaseTreeView::SelectionListSavingEnumeratorCallback(PRUint32 aIndex,
166  const nsAString& aId,
167  const nsAString& aGuid,
168  void* aUserData)
169 {
170  NS_ENSURE_ARG_POINTER(aUserData);
171 
172  sbSelectionList* list = static_cast<sbSelectionList*>(aUserData);
173  NS_ENSURE_STATE(list);
174 
175  nsString guid(aGuid);
176  PRBool success = list->Put(aId, guid);
177  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
178 
179  return NS_OK;
180 }
181 
182 /* static */ nsresult PR_CALLBACK
183 sbLocalDatabaseTreeView::SelectionListGuidsEnumeratorCallback(PRUint32 aIndex,
184  const nsAString& aId,
185  const nsAString& aGuid,
186  void* aUserData)
187 {
188  NS_ENSURE_ARG_POINTER(aUserData);
189 
190  nsTArray<nsString>* list = static_cast<nsTArray<nsString>*>(aUserData);
191  NS_ENSURE_STATE(list);
192 
193  nsString* appended = list->AppendElement(aGuid);
194  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
195 
196  return NS_OK;
197 }
198 
200  nsIClassInfo,
201  nsIObserver,
203  nsITreeView,
210 
211 NS_IMPL_CI_INTERFACE_GETTER9(sbLocalDatabaseTreeView,
213  nsIObserver,
214  nsITreeView,
221 
222 sbLocalDatabaseTreeView::sbLocalDatabaseTreeView() :
223  mListType(eLibrary),
224  mMediaListView(nsnull),
225  mViewSelection(nsnull),
226  mArrayLength(0),
227  mManageSelection(PR_FALSE),
228  mHaveSavedSelection(PR_FALSE),
229  mMouseState(sbILocalDatabaseTreeView::MOUSE_STATE_NONE),
230  mMouseStateRow(-1),
231  mSelectionIsAll(PR_FALSE),
232  mFakeAllRow(PR_FALSE),
233  mIsListeningToPlayback(PR_FALSE),
234  mShouldPreventRebuild(PR_FALSE),
235  mFirstCachedRow(NOT_SET),
236  mLastCachedRow(NOT_SET),
237  mPlayQueueIndex(0)
238 {
239 #ifdef PR_LOGGING
240  if (!gLocalDatabaseTreeViewLog) {
241  gLocalDatabaseTreeViewLog = PR_NewLogModule("sbLocalDatabaseTreeView");
242  }
243 #endif
244 }
245 
247 {
248  nsresult rv;
249 
250  if (mPlayQueueService) {
251 
252  nsCOMPtr<sbIPlayQueueServiceListener> playQueueServiceListener =
253  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseTreeView*, this),
254  &rv);
255  if (NS_SUCCEEDED(rv)) {
256  mPlayQueueService->RemoveListener(playQueueServiceListener);
257  }
258  }
259 
260  if (mViewSelection) {
261  nsCOMPtr<sbIMediaListViewSelectionListener> selectionListener =
262  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseTreeView*, this),
263  &rv);
264  if (NS_SUCCEEDED(rv))
265  mViewSelection->RemoveListener(selectionListener);
266  }
267 
268  // Remove observer for treeview invalidations
269  nsCOMPtr<nsIObserverService> observerService =
270  do_GetService("@mozilla.org/observer-service;1", &rv);
271 
272  if (NS_SUCCEEDED(rv)) {
273  observerService->RemoveObserver(this, SB_INVALIDATE_ALL_TREEVIEWS_TOPIC);
274  }
275 
276  NS_ASSERTION(!mIsListeningToPlayback, "Still listening when dtor called");
277  // declare ourselves dead, in case the async listener is actually in the
278  // process of waiting to fire. yay.
280 }
281 
282 nsresult
285  sbIPropertyArray* aCurrentSort,
287 {
288  NS_ENSURE_ARG_POINTER(aMediaListView);
289  NS_ENSURE_ARG_POINTER(aArray);
290 
291  nsresult rv;
292 
293  // Make sure we actually are getting a sort or a state
294  if (aCurrentSort) {
295  NS_ENSURE_TRUE(!aState, NS_ERROR_INVALID_ARG);
296  PRUint32 arrayLength;
297  rv = aCurrentSort->GetLength(&arrayLength);
298  NS_ENSURE_SUCCESS(rv, rv);
299  NS_ENSURE_STATE(arrayLength);
300  }
301  else {
302  NS_ENSURE_ARG_POINTER(aState);
303  }
304 
305  mPropMan = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
306  NS_ENSURE_SUCCESS(rv, rv);
307 
308  mMediaListView = aMediaListView;
309  mArray = aArray;
310 
311  // Determine the list type
312  PRBool isDistinct;
313  rv = mArray->GetIsDistinct(&isDistinct);
314  NS_ENSURE_SUCCESS(rv, rv);
315 
316  if (isDistinct) {
317  mListType = eDistinct;
318  mManageSelection = PR_TRUE;
319  mFakeAllRow = PR_TRUE;
320  mSelectionIsAll = PR_TRUE;
321  }
322  else {
323  mManageSelection = PR_FALSE;
324  nsString baseTable;
325  rv = mArray->GetBaseTable(baseTable);
326  NS_ENSURE_SUCCESS(rv, rv);
327 
328  if (baseTable.EqualsLiteral("media_items")) {
329  mListType = eLibrary;
330  }
331  else {
332  if (baseTable.EqualsLiteral("simple_media_lists")) {
333  mListType = eSimple;
334  }
335  else {
336  return NS_ERROR_UNEXPECTED;
337  }
338  }
339  }
340 
341  // If we are not managing our selection, we rely on our view for selection
342  // management. Add a listener to the view's selection so we can track
343  // changes
344  if (!mManageSelection) {
345  nsCOMPtr<sbIMediaListViewSelection> viewSelection;
346  rv = mMediaListView->GetSelection(getter_AddRefs(viewSelection));
347  NS_ENSURE_TRUE(viewSelection, NS_ERROR_UNEXPECTED);
348  mViewSelection = viewSelection;
349 
350  nsCOMPtr<sbIMediaListViewSelectionListener> selectionListener =
351  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseTreeView*, this), &rv);
352  NS_ENSURE_SUCCESS(rv, rv);
353 
354  rv = mViewSelection->AddListener(selectionListener);
355  NS_ENSURE_SUCCESS(rv, rv);
356  }
357 
358  rv = mArray->GetPropertyCache(getter_AddRefs(mPropertyCache));
359  NS_ENSURE_SUCCESS(rv, rv);
360 
361  nsCOMPtr<sbILocalDatabaseGUIDArrayListener> listener =
362  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseTreeView*, this), &rv);
363  NS_ENSURE_SUCCESS(rv, rv);
364 
365  rv = mArray->SetListener(listener);
366  NS_ENSURE_SUCCESS(rv, rv);
367 
368  rv = mArray->GetFetchSize(&mFetchSize);
369  NS_ENSURE_SUCCESS(rv, rv);
370 
371  PRBool success = mSelectionList.Init();
372  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
373  mHaveSavedSelection = PR_FALSE;
374 
375  if (aState) {
376 #ifdef DEBUG
377  {
378  nsString buff;
379  aState->ToString(buff);
380  LOG(("sbLocalDatabaseTreeView::Init[0x%.8x] - restoring %s",
381  this, NS_LossyConvertUTF16toASCII(buff).get()));
382  }
383 #endif
384  rv = aState->mSort->GetProperty(mCurrentSortProperty);
385  NS_ENSURE_SUCCESS(rv, rv);
386 
387  PRBool isAscending;
388  rv = aState->mSort->GetIsAscending(&isAscending);
389  NS_ENSURE_SUCCESS(rv, rv);
390  mCurrentSortDirectionIsAscending = isAscending;
391 
392  if (mManageSelection) {
393  mSelectionIsAll = aState->mSelectionIsAll;
394  if (!mSelectionIsAll) {
395  aState->mSelectionList.EnumerateRead(SB_CopySelectionListCallback,
396  &mSelectionList);
397  mHaveSavedSelection = PR_TRUE;
398  }
399  }
400  }
401  else {
402  // Grab the top level sort property from the bag
403  nsCOMPtr<sbIProperty> property;
404  rv = aCurrentSort->GetPropertyAt(0, getter_AddRefs(property));
405  NS_ENSURE_SUCCESS(rv, rv);
406 
407  rv = property->GetId(mCurrentSortProperty);
408  NS_ENSURE_SUCCESS(rv, rv);
409 
410  nsAutoString value;
411  rv = property->GetValue(value);
412  NS_ENSURE_SUCCESS(rv, rv);
413 
414  mCurrentSortDirectionIsAscending = value.EqualsLiteral("a");
415  }
416 
417  // Get our localized strings
418  nsCOMPtr<nsIStringBundleService> bundleService =
419  do_GetService("@mozilla.org/intl/stringbundle;1", &rv);
420  NS_ENSURE_SUCCESS(rv, rv);
421 
422  nsCOMPtr<nsIStringBundle> stringBundle;
423  rv = bundleService->CreateBundle(SB_STRING_BUNDLE_CHROME_URL,
424  getter_AddRefs(stringBundle));
425  NS_ENSURE_SUCCESS(rv, rv);
426 
427  rv = stringBundle->GetStringFromName(NS_LITERAL_STRING("library.all").get(),
428  getter_Copies(mLocalizedAll));
429  if (NS_FAILED(rv)) {
430  mLocalizedAll.AssignLiteral("library.all");
431  }
432 
433  // If this is not a distinct list, we will want a listener to track playback
434  // changes. This listener should be set up in SetTree.
435  if (mListType != eDistinct) {
436  nsCOMPtr<nsISupportsWeakReference> weakRef =
437  do_GetService("@songbirdnest.com/Songbird/Mediacore/Manager;1", &rv);
438  NS_ENSURE_SUCCESS(rv, rv);
439 
440  rv = weakRef->GetWeakReference(getter_AddRefs(mMediacoreManager));
441  NS_ENSURE_SUCCESS(rv, rv);
442  }
443 
444  // If this is a view of the play queue, keep a reference to the queue service.
445  mPlayQueueService =
446  do_GetService("@songbirdnest.com/Songbird/playqueue/service;1", &rv);
447  NS_ENSURE_SUCCESS(rv, rv);
448 
449  nsCOMPtr<sbIMediaList> queueList;
450  rv = mPlayQueueService->GetMediaList(getter_AddRefs(queueList));
451  NS_ENSURE_SUCCESS(rv, rv);
452 
453  nsCOMPtr<sbIMediaList> viewList;
454  rv = mMediaListView->GetMediaList(getter_AddRefs(viewList));
455  NS_ENSURE_SUCCESS(rv, rv);
456 
457  PRBool isViewOfQueue;
458  rv = queueList->Equals(viewList, &isViewOfQueue);
459  NS_ENSURE_SUCCESS(rv, rv);
460 
461  if (isViewOfQueue) {
462  rv = mPlayQueueService->GetIndex(&mPlayQueueIndex);
463  NS_ENSURE_SUCCESS(rv, rv);
464 
465  // add listener
466  nsCOMPtr<sbIPlayQueueServiceListener> playQueueServiceListener =
467  do_QueryInterface(NS_ISUPPORTS_CAST(sbILocalDatabaseTreeView*, this), &rv);
468  NS_ENSURE_SUCCESS(rv, rv);
469 
470  rv = mPlayQueueService->AddListener(playQueueServiceListener);
471  NS_ENSURE_SUCCESS(rv, rv);
472  } else {
473  // If this isn't a view of the queue, set mPlayQueueService to null so we
474  // don't attempt to set play queue status properties
475  mPlayQueueService = nsnull;
476  }
477 
478  // Attempt to get a device for the list we are currently viewing
479  nsCOMPtr<sbIDeviceManager2> deviceManager =
480  do_GetService("@songbirdnest.com/Songbird/DeviceManager;2", &rv);
481  NS_ENSURE_SUCCESS(rv, rv);
482 
483  nsCOMPtr<sbIDevice> device;
484  rv = deviceManager->GetDeviceForItem(viewList, getter_AddRefs(device));
485  if (NS_FAILED(rv) || !device) {
486  // Couldn't find a device so must not be viewing device content
487  mViewingDeviceContent = PR_FALSE;
488  }
489  else {
490  // Found a device for this medialist, must be viewing device content
491  mViewingDeviceContent = PR_TRUE;
492  }
493 
494  // Add observer for treeview invalidations
495  nsCOMPtr<nsIObserverService> observerService =
496  do_GetService("@mozilla.org/observer-service;1", &rv);
497  NS_ENSURE_SUCCESS(rv, rv);
498 
499  rv = observerService->AddObserver(this,
501  PR_FALSE);
502  NS_ENSURE_SUCCESS(rv, rv);
503 
504  return NS_OK;
505 }
506 
507 nsresult
509 {
510  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - Rebuild()", this));
511 
512  if (mShouldPreventRebuild) {
513  LOG(("sbLocalDatabaseTreeView - Rebuild() mShouldPreventRebuild == true"
514  ", skipping rebuild."));
515  return NS_OK;
516  }
517 
518  nsresult rv;
519 
520  // Check to see if the sort of the underlying array has changed
521  nsCOMPtr<sbIPropertyArray> sort;
522  rv = mArray->GetCurrentSort(getter_AddRefs(sort));
523  NS_ENSURE_SUCCESS(rv, rv);
524 
525  nsCOMPtr<sbIProperty> property;
526  rv = sort->GetPropertyAt(0, getter_AddRefs(property));
527  NS_ENSURE_SUCCESS(rv, rv);
528 
529  nsString arraySortProperty;
530  rv = property->GetId(arraySortProperty);
531  NS_ENSURE_SUCCESS(rv, rv);
532 
533  PRBool arraySortDirectionIsAscending;
534  nsString direction;
535  rv = property->GetValue(direction);
536  NS_ENSURE_SUCCESS(rv, rv);
537  arraySortDirectionIsAscending = direction.EqualsLiteral("a");
538 
539  if (!arraySortProperty.Equals(mCurrentSortProperty) ||
540  arraySortDirectionIsAscending != mCurrentSortDirectionIsAscending) {
541 
542  mCurrentSortProperty = arraySortProperty;
543  mCurrentSortDirectionIsAscending = arraySortDirectionIsAscending;
544 
545  rv = UpdateColumnSortAttributes(arraySortProperty,
546  arraySortDirectionIsAscending);
547  NS_ENSURE_SUCCESS(rv, rv);
548  }
549 
550  // Update the number of rows in the tree widget
551  PRInt32 oldRowCount;
552  rv = GetRowCount(&oldRowCount);
553  NS_ENSURE_SUCCESS(rv, rv);
554  rv = mArray->GetLength(&mArrayLength);
555  NS_ENSURE_SUCCESS(rv, rv);
556  PRInt32 newRowCount;
557  rv = GetRowCount(&newRowCount);
558  NS_ENSURE_SUCCESS(rv, rv);
559 
560  if (mTreeBoxObject) {
561  // Change the number of rows in the tree by the difference between the
562  // new row count and the old cached row count. We still need to invalidate
563  // the whole tree since we don't know where the row changes took place.
564  PRInt32 delta = newRowCount - oldRowCount;
565 
566  sbAutoUpdateBatch autoBatch(mTreeBoxObject);
567  if (delta != 0) {
568  rv = mTreeBoxObject->RowCountChanged(oldRowCount, delta);
569  NS_ENSURE_SUCCESS(rv, rv);
570  }
571  rv = mTreeBoxObject->Invalidate();
572  NS_ENSURE_SUCCESS(rv, rv);
573  }
574 
575  // If we're managing the selection, restore it after adjusting the tree box
576  // row count
577  if (mManageSelection) {
578  RestoreSelection();
579  }
580 
581  return NS_OK;
582 }
583 
584 void
586 {
587  mShouldPreventRebuild = aShouldPreventRebuild;
588 }
589 
590 PRBool
592 {
593  return mShouldPreventRebuild;
594 }
595 
596 void
598 {
599  mMediaListView = nsnull;
600  mViewSelection = nsnull;
601 }
602 
609 nsresult
610 sbLocalDatabaseTreeView::TokenizeProperties(const nsAString& aProperties,
611  nsISupportsArray* aAtomArray)
612 {
613  NS_ASSERTION(!aProperties.IsEmpty(), "Don't give this an empty string");
614  NS_ASSERTION(aAtomArray, "Null pointer!");
615 
616  const PRUnichar* current, *end;
617  aProperties.BeginReading(&current, &end);
618 
619  static const PRUnichar sSpaceChar = ' ';
620 
621  nsresult rv;
622  nsCOMPtr<nsIAtomService> atomService =
623  do_GetService(NS_ATOMSERVICE_CONTRACTID, &rv);
624  NS_ENSURE_SUCCESS(rv, rv);
625 
626  do {
627 
628  // Skip whitespace
629  while (current < end && *current == sSpaceChar) {
630  ++current;
631  }
632 
633  // If only whitespace, we're done
634  if (current == end) {
635  break;
636  }
637 
638  // Note the first non-whitespace character
639  const PRUnichar* firstChar = current;
640 
641  // Advance to the next whitespace character
642  while (current < end && *current != sSpaceChar) {
643  ++current;
644  }
645 
646  nsString token(Substring(firstChar, current));
647 
648  // Make an atom
649  nsCOMPtr<nsIAtom> atom;
650  rv = atomService->GetAtom(token, getter_AddRefs(atom));
651  NS_ENSURE_SUCCESS(rv, rv);
652 
653  // Don't encourage people to step on each other's toes.
654  if (aAtomArray->IndexOf(atom) != -1) {
655  continue;
656  }
657 
658  rv = aAtomArray->AppendElement(atom);
659  NS_ENSURE_SUCCESS(rv, rv);
660 
661  } while (current < end);
662 
663  return NS_OK;
664 }
665 
666 inline
667 PRBool intersection(PRInt32 start1, PRInt32 end1, PRInt32 start2, PRInt32 end2, PRInt32 & intersectStart, PRInt32 & intersectEnd) {
668  PRBool const result = end1 >= start2 && start1 <= end2;
669  if (result) {
670  intersectStart = start2;
671  intersectEnd = end2;
672  if (start1 > start2)
673  intersectStart = start1;
674  if (end1 < end2)
675  intersectEnd = end1;
676 
677  }
678  return result;
679 }
680 
681 nsresult
682 sbLocalDatabaseTreeView::GetCellPropertyValue(PRInt32 aIndex,
683  nsITreeColumn *aTreeColumn,
684  nsAString& _retval)
685 {
686  NS_ENSURE_ARG_POINTER(aTreeColumn);
687 
688  nsresult rv;
689 
691  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
692  // cause events to be fired, or use synchronous proxies, as you risk //
693  // crashing in recursive painting/frame construction. //
695 
696  nsString bind;
697  rv = GetPropertyForTreeColumn(aTreeColumn, bind);
698  NS_ENSURE_SUCCESS(rv, rv);
699 
700  // If this is an ordinal column, return just the row number
701  if (bind.EqualsLiteral(SB_PROPERTY_ORDINAL)) {
702  _retval.AppendInt(aIndex + 1);
703  return NS_OK;
704  }
705 
706  // Explicitly cache all of the properties of all the visible rows
707  if (mTreeBoxObject) {
708  PRInt32 first;
709  PRInt32 last;
710  rv = mTreeBoxObject->GetFirstVisibleRow(&first);
711  NS_ENSURE_SUCCESS(rv, rv);
712  rv = mTreeBoxObject->GetLastVisibleRow(&last);
713  NS_ENSURE_SUCCESS(rv, rv);
714 
715  // If we have a valid first/last and they're different
716  if (first >= 0 && last >= 0) {
717 
718  // Calculate the number of rows we're going process
719  PRInt32 length = last - first + 1;
720 
721  if (mFirstCachedRow != NOT_SET && mFirstCachedRow > first) {
722  // User scrolled up, make sure to cache at least a page
723  first = std::max(std::min(first, mFirstCachedRow - length + 1),
724  0);
725  }
726  if (mLastCachedRow != NOT_SET && mLastCachedRow < last) {
727  // User scrolled down, make sure to cache at least a page
728  last = std::min(std::max(last, mLastCachedRow + length - 1),
729  (PRInt32)mArrayLength + (mFakeAllRow ? 1 : 0) - 1);
730  }
731  length = last - first + 1;
732 
733  PRInt32 intersectStart;
734  PRInt32 intersectEnd;
735  // Get the intersection between the current first and last and previous
736  PRBool const intersects = intersection(first, last,
737  mFirstCachedRow, mLastCachedRow,
738  intersectStart, intersectEnd);
739  // If they intersect then remove the intersecting rows from the length
740  if (intersects) {
741  length -= (intersectEnd - intersectStart + 1);
742  }
743  if (length > 0) {
744  // Set the capacity
745  mGuidWorkArray.SetCapacity(length);
746  mGuidWorkArray.Reset();
747  for (PRInt32 row = first;
748  row <= last && static_cast<PRUint32>(row) < mArrayLength;
749  row++) {
750 
751  // Skip the fake row and any row's we have already cached
752  if ((row >= mFirstCachedRow && row <= mLastCachedRow) ||
753  (mFakeAllRow && row == 0)) {
754  continue;
755  }
756 
757  nsString guid;
758  rv = mArray->GetGuidByIndex(TreeToArray(row), guid);
759  NS_ENSURE_SUCCESS(rv, rv);
760 
761  rv = mGuidWorkArray.Append(guid);
762  NS_ENSURE_SUCCESS(rv, rv);
763  }
764 
765  rv = mPropertyCache->CacheProperties(const_cast<PRUnichar const **>(
766  mGuidWorkArray.AsCharArray()),
767  mGuidWorkArray.Length());
768  NS_ENSURE_SUCCESS(rv, rv);
769 
770  mFirstCachedRow = first;
771  mLastCachedRow = last;
772  }
773  }
774  }
775 
776  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
777  rv = GetBag(aIndex, getter_AddRefs(bag));
778  NS_ENSURE_SUCCESS(rv, rv);
779 
780  nsString value;
781  rv = bag->GetProperty(bind, value);
782  NS_ENSURE_SUCCESS(rv, rv);
783 
784  // Get the property info
785  nsCOMPtr<sbIPropertyInfo> info;
786  rv = mPropMan->GetPropertyInfo(bind, getter_AddRefs(info));
787  NS_ENSURE_SUCCESS(rv, rv);
788 
789  // Get the unit converter
790  nsCOMPtr<sbIPropertyUnitConverter> converter;
791  rv = info->GetUnitConverter(getter_AddRefs(converter));
792  NS_ENSURE_SUCCESS(rv, rv);
793 
794  // Format the value for display
795  if (converter) {
796  // unit converter present, ask it to format the value using the best unit,
797  // using at most 1 decimal digit
798  rv = converter->AutoFormat(value,
799  -1, // no min decimals
800  1, // 1 decimal max
801  _retval);
802  } else {
803  // no unit converter, just use propertyinfo.format
804  rv = info->Format(value, _retval);
805  }
806 
807  if (NS_FAILED(rv)) {
808  _retval.Truncate();
809  }
810 
811  return NS_OK;
812 }
813 
814 
815 nsresult
816 sbLocalDatabaseTreeView::SaveSelectionList()
817 {
818  NS_ASSERTION(mManageSelection,
819  "SaveSelectionList() called but we're not managing selection");
820 
821  // Do nothing if selection already saved
822  if (mHaveSavedSelection)
823  return NS_OK;
824 
825  // If the current selection is everything, don't save anything
826  if (mSelectionIsAll) {
827  return NS_OK;
828  }
829 
830  nsresult rv = EnumerateSelection(SelectionListSavingEnumeratorCallback,
831  &mSelectionList);
832 
833  NS_ENSURE_SUCCESS(rv, rv);
834 
835  mHaveSavedSelection = PR_TRUE;
836 
837  return NS_OK;
838 }
839 
840 
841 nsresult
842 sbLocalDatabaseTreeView::RestoreSelection()
843 {
844  NS_ASSERTION
845  (mManageSelection,
846  "RestoreSelection() called but we're not managing selection");
847 
848  // If no selection is set, don't restore anything
849  if (!mRealSelection) {
850  return NS_OK;
851  }
852 
853  nsresult rv;
854  if (mSelectionIsAll) {
855  rv = mRealSelection->Select(0);
856  NS_ENSURE_SUCCESS(rv, rv);
857  }
858  else if (mHaveSavedSelection) {
859  // Start with an empty selection
860  rv = mRealSelection->ClearSelection();
861  NS_ENSURE_SUCCESS(rv, rv);
862 
863  // Check each item to see if it needs to be selected. Enumerating the
864  // selection list can't be done because it's not possible to get the array
865  // index from a unique ID.
866  for (PRUint32 i = 0; i < mArrayLength; i++) {
867  // Stop if nothing more to select
868  if (!mSelectionList.Count()) {
869  break;
870  }
871 
872  // Get the unique ID for the current item
873  nsString id;
874  rv = GetUniqueIdForIndex(i, id);
875  NS_ENSURE_SUCCESS(rv, rv);
876 
877  // Select item if it's in the selection list
878  if (mSelectionList.Get(id, nsnull)) {
879  // Don't apply this selection any more
880  mSelectionList.Remove(id);
881 
882  // Select row in the real selection
883  PRInt32 row = ArrayToTree(i);
884  rv = mRealSelection->ToggleSelect(row);
885  NS_ENSURE_SUCCESS(rv, rv);
886  }
887  }
888  // Normally we have reselected everything, and therefore, mSelectionList is
889  // empty. However if an item has either disappeared or been renamed, it'll
890  // still be in the list at this point, and this will interfere with
891  // GetSelectedValues (it'll think those values are still selected). So force
892  // the array to be empty now.
893  mSelectionList.Clear();
894  mHaveSavedSelection = PR_FALSE;
895  }
896 
897  return NS_OK;
898 }
899 
900 nsresult
901 sbLocalDatabaseTreeView::EnumerateSelection(sbSelectionEnumeratorCallbackFunc aFunc,
902  void* aUserData)
903 {
904  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - EnumerateSelection()", this));
905 
906  if (mRealSelection) {
907 
908  // Iterate through the selection's ranges and store the unique identifier
909  // for each row in the selection hash table. Note that we don't want
910  // to clear the selection list first as to keep items that were previously
911  // selected but were never re-selected by the background cache fill
912  PRInt32 rangeCount;
913  nsresult rv = mRealSelection->GetRangeCount(&rangeCount);
914  NS_ENSURE_SUCCESS(rv, rv);
915 
916  for (PRInt32 i = 0; i < rangeCount; i++) {
917  PRInt32 min;
918  PRInt32 max;
919  rv = mRealSelection->GetRangeAt(i, &min, &max);
920  NS_ENSURE_SUCCESS(rv, rv);
921 
922  if (min >= 0 && max >= 0) {
923  for (PRInt32 j = min; j <= max; j++) {
924 
925  if (IsAllRow(j)) {
926  continue;
927  }
928 
929  nsString id;
930  PRUint32 index = TreeToArray(j);
931  rv = GetUniqueIdForIndex(index, id);
932  NS_ENSURE_SUCCESS(rv, rv);
933 
934  nsString guid;
935  rv = mArray->GetGuidByIndex(index, guid);
936  NS_ENSURE_SUCCESS(rv, rv);
937 
938  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SaveSelectionList() - "
939  "saving %s from index %d guid %s",
940  this, NS_ConvertUTF16toUTF8(id).get(), index,
941  NS_ConvertUTF16toUTF8(guid).get()));
942 
943  rv = aFunc(index, id, guid, aUserData);
944  NS_ENSURE_SUCCESS(rv, rv);
945  }
946  }
947  else {
948  NS_WARNING("Bad value returned from nsTreeSelection::GetRangeAt");
949  }
950  }
951 
952  }
953 
954  return NS_OK;
955 }
956 
957 nsresult
958 sbLocalDatabaseTreeView::GetUniqueIdForIndex(PRUint32 aIndex, nsAString& aId)
959 {
960  nsresult rv;
961 
962  aId.Truncate();
963 
964  // For distinct lists, the sort key works as a unique id
965  if (mListType == eDistinct) {
966  rv = mArray->GetSortPropertyValueByIndex(aIndex, aId);
967  NS_ENSURE_SUCCESS(rv, rv);
968  }
969  else {
970  // For regular lists, the unique identifer is composed of the lists' guid
971  // appended to the item's guid appeneded to the item's database rowid.
972  nsCOMPtr<sbIMediaList> list;
973  rv = mMediaListView->GetMediaList(getter_AddRefs(list));
974  NS_ENSURE_SUCCESS(rv, rv);
975 
976  nsString guid;
977  rv = list->GetGuid(guid);
978  NS_ENSURE_SUCCESS(rv, rv);
979  aId.Append(guid);
980  aId.Append('|');
981 
982  guid.Truncate();
983  rv = mArray->GetGuidByIndex(aIndex, guid);
984  NS_ENSURE_SUCCESS(rv, rv);
985  aId.Append(guid);
986  aId.Append('|');
987 
988  PRUint64 rowid;
989  rv = mArray->GetRowidByIndex(aIndex, &rowid);
990  NS_ENSURE_SUCCESS(rv, rv);
991  AppendInt(aId, rowid);
992  }
993 
994  return NS_OK;
995 }
996 
997 void
998 sbLocalDatabaseTreeView::SetSelectionIsAll(PRBool aSelectionIsAll) {
999 
1000  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetSelectionIsAll(%d)",
1001  this, aSelectionIsAll));
1002  NS_ASSERTION(mManageSelection,
1003  "SetSelectionIsAll() called but we're not managing selection");
1004 
1005  mSelectionIsAll = aSelectionIsAll;
1006 
1007  // An all selection means everything in our selection list was implicitly
1008  // selected so we can clear it
1009  if (mSelectionIsAll) {
1010  ClearSelectionList();
1011  }
1012 
1013 }
1014 
1015 void
1016 sbLocalDatabaseTreeView::ClearSelectionList() {
1017 
1018  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - ClearSelectionList()", this));
1019  NS_ASSERTION(mManageSelection,
1020  "ClearSelectionList() called but we're not managing selection");
1021 
1022  mSelectionList.Clear();
1023  mHaveSavedSelection = PR_FALSE;
1024 }
1025 
1026 nsresult
1027 sbLocalDatabaseTreeView::UpdateColumnSortAttributes(const nsAString& aProperty,
1028  PRBool aDirection)
1029 {
1030  nsresult rv;
1031 
1032  if (!mTreeBoxObject) {
1033  NS_WARNING("Unable to update column sort attributes, no box object!");
1034  return NS_OK;
1035  }
1036 
1037  nsCOMPtr<nsITreeColumns> columns;
1038  rv = mTreeBoxObject->GetColumns(getter_AddRefs(columns));
1039  NS_ENSURE_SUCCESS(rv, rv);
1040 
1041  PRInt32 count;
1042  rv = columns->GetCount(&count);
1043  NS_ENSURE_SUCCESS(rv, rv);
1044 
1045  for (PRInt32 i = 0; i < count; i++) {
1046  nsCOMPtr<nsITreeColumn> column;
1047  rv = columns->GetColumnAt(i, getter_AddRefs(column));
1048  NS_ENSURE_SUCCESS(rv, rv);
1049 
1050  if (!column) {
1051  NS_WARNING("Failed to find column!");
1052  continue;
1053  }
1054 
1055  nsCOMPtr<nsIDOMElement> element;
1056  rv = column->GetElement(getter_AddRefs(element));
1057  NS_ENSURE_SUCCESS(rv, rv);
1058 
1059  nsString bind;
1060  rv = element->GetAttribute(NS_LITERAL_STRING("bind"), bind);
1061  NS_ENSURE_SUCCESS(rv, rv);
1062 
1063  if (bind.Equals(aProperty)) {
1064  rv = element->SetAttribute(NS_LITERAL_STRING("sortActive"),
1065  NS_LITERAL_STRING("true"));
1066  NS_ENSURE_SUCCESS(rv, rv);
1067 
1068  nsString direction;
1069  if (aDirection) {
1070  direction.AssignLiteral("ascending");
1071  }
1072  else {
1073  direction.AssignLiteral("descending");
1074  }
1075  rv = element->SetAttribute(NS_LITERAL_STRING("sortDirection"),
1076  direction);
1077  NS_ENSURE_SUCCESS(rv, rv);
1078  }
1079  else {
1080  rv = element->RemoveAttribute(NS_LITERAL_STRING("sortActive"));
1081  NS_ENSURE_SUCCESS(rv, rv);
1082  rv = element->RemoveAttribute(NS_LITERAL_STRING("sortDirection"));
1083  NS_ENSURE_SUCCESS(rv, rv);
1084  }
1085  }
1086 
1087  return NS_OK;
1088 }
1089 
1090 nsresult
1092 {
1093  NS_ENSURE_ARG_POINTER(aState);
1094 
1095  nsresult rv;
1096 
1097  nsRefPtr<sbLocalDatabaseTreeViewState> state =
1099  NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY);
1100 
1101  rv = state->Init();
1102  NS_ENSURE_SUCCESS(rv, rv);
1103 
1104  state->mSort = do_CreateInstance(SONGBIRD_LIBRARYSORT_CONTRACTID, &rv);
1105  NS_ENSURE_SUCCESS(rv, rv);
1106 
1107  rv = state->mSort->Init(mCurrentSortProperty,
1108  mCurrentSortDirectionIsAscending);
1109  NS_ENSURE_SUCCESS(rv, rv);
1110 
1111  if (mSelectionIsAll) {
1112  state->mSelectionIsAll = PR_TRUE;
1113  }
1114  else {
1115  mSelectionList.EnumerateRead(SB_CopySelectionListCallback,
1116  &state->mSelectionList);
1117  rv = EnumerateSelection(SelectionListSavingEnumeratorCallback,
1118  &state->mSelectionList);
1119  NS_ENSURE_SUCCESS(rv, rv);
1120  }
1121 
1122 #ifdef DEBUG
1123  {
1124  nsString buff;
1125  state->ToString(buff);
1126  LOG(("sbLocalDatabaseTreeView::GetState[0x%.8x] - returning %s",
1127  this, NS_LossyConvertUTF16toASCII(buff).get()));
1128  }
1129 #endif
1130 
1131  state.forget(aState);
1132  return NS_OK;
1133 }
1134 
1135 // sbILocalDatabaseTreeView
1136 NS_IMETHODIMP
1137 sbLocalDatabaseTreeView::SetSort(const nsAString& aProperty, PRBool aDirection)
1138 {
1139  LOG(("sbLocalDatabaseTreeView[0x%.8x] - SetSort(%s, %d)",
1140  this, NS_LossyConvertUTF16toASCII(aProperty).get(), aDirection));
1141 
1142  nsresult rv;
1143 
1144  nsCOMPtr<sbIMediaList> list;
1145  rv = mMediaListView->GetMediaList(getter_AddRefs(list));
1146  NS_ENSURE_SUCCESS(rv, rv);
1147 
1148  nsString isSortable;
1149  rv = list->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISSORTABLE), isSortable);
1150  NS_ENSURE_SUCCESS(rv, rv);
1151 
1152  if (isSortable.Equals(NS_LITERAL_STRING("0"))) {
1153  // list is not sortable
1154  return NS_ERROR_FAILURE;
1155  }
1156 
1157  // If this the library, a sort on ordinal is replaced with a create date
1158  // sort since the library does not have an ordinal.
1159  nsString sortProperty;
1160  sortProperty = aProperty;
1161  if (mListType == eLibrary && aProperty.EqualsLiteral(SB_PROPERTY_ORDINAL)) {
1162  sortProperty.AssignLiteral(SB_PROPERTY_CREATED);
1163  }
1164 
1165  // If we are linked to a media list view, use its interfaces to manage
1166  // the sort
1167  if (mListType != eDistinct) {
1168  NS_ENSURE_STATE(mMediaListView);
1169 
1170  nsCOMPtr<sbIMutablePropertyArray> newSort =
1171  do_CreateInstance(SB_MUTABLEPROPERTYARRAY_CONTRACTID, &rv);
1172  NS_ENSURE_SUCCESS(rv, rv);
1173 
1174  rv = newSort->SetStrict(PR_FALSE);
1175  NS_ENSURE_SUCCESS(rv, rv);
1176 
1177  rv = newSort->AppendProperty(aProperty,
1178  aDirection ?
1179  NS_LITERAL_STRING("a") :
1180  NS_LITERAL_STRING("d"));
1181  NS_ENSURE_SUCCESS(rv, rv);
1182 
1183  nsCOMPtr<sbISortableMediaListView> sortable =
1184  do_QueryInterface(NS_ISUPPORTS_CAST(sbISortableMediaListView*, mMediaListView), &rv);
1185  NS_ENSURE_SUCCESS(rv, rv);
1186 
1187  rv = sortable->SetSort(newSort);
1188  NS_ENSURE_SUCCESS(rv, rv);
1189  }
1190  else {
1191  // Suppress invalidation of the underlying GUID array.
1192  sbAutoSuppressArrayInvalidation suppress(mArray);
1193 
1194  rv = mArray->ClearSorts();
1195  NS_ENSURE_SUCCESS(rv, rv);
1196 
1197  rv = mArray->AddSort(sortProperty, aDirection);
1198  NS_ENSURE_SUCCESS(rv, rv);
1199 
1200  rv = mArray->Invalidate(PR_FALSE);
1201  NS_ENSURE_SUCCESS(rv, rv);
1202  }
1203 
1204  mCurrentSortProperty = aProperty;
1205  mCurrentSortDirectionIsAscending = aDirection;
1206 
1207  rv = UpdateColumnSortAttributes(aProperty, aDirection);
1208  NS_ENSURE_SUCCESS(rv, rv);
1209 
1210  return NS_OK;
1211 }
1212 
1213 NS_IMETHODIMP
1214 sbLocalDatabaseTreeView::InvalidateRowsByGuid(const nsAString& aGuid)
1215 {
1216  LOG(("sbLocalDatabaseTreeView[0x%.8x] - InvalidateRowsByGuid(%s)",
1217  this, NS_LossyConvertUTF16toASCII(aGuid).get()));
1218 
1219  if (mTreeBoxObject) {
1220  PRInt32 first;
1221  PRInt32 last;
1222  nsresult rv = mTreeBoxObject->GetFirstVisibleRow(&first);
1223  NS_ENSURE_SUCCESS(rv, rv);
1224  rv = mTreeBoxObject->GetLastVisibleRow(&last);
1225  NS_ENSURE_SUCCESS(rv, rv);
1226 
1227  if (first >= 0 && last >= 0) {
1228  PRUint32 length;
1229  rv = mArray->GetLength(&length);
1230  NS_ENSURE_SUCCESS(rv, rv);
1231 
1232  PRInt32 rowCount = ArrayToTree(length);
1233 
1234  if (last >= rowCount)
1235  last = rowCount - 1;
1236 
1237  for (PRInt32 row = first; row <= last; row++) {
1238  nsString guid;
1239  rv = mArray->GetGuidByIndex(TreeToArray(row), guid);
1240  NS_ENSURE_SUCCESS(rv, rv);
1241  if (guid.Equals(aGuid)) {
1242  rv = mTreeBoxObject->InvalidateRow(row);
1243  NS_ENSURE_SUCCESS(rv, rv);
1244  }
1245  }
1246  }
1247 
1248  }
1249 
1250  return NS_OK;
1251 }
1252 
1253 NS_IMETHODIMP
1254 sbLocalDatabaseTreeView::SetMouseState(PRInt32 aRow,
1255  nsITreeColumn* aColumn,
1256  PRUint32 aState)
1257 {
1258 #ifdef PR_LOGGING
1259  PRInt32 colIndex = -1;
1260  if (aColumn) {
1261  aColumn->GetIndex(&colIndex);
1262  }
1263  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetMouseState(%d, %d, %d)", this,
1264  aRow, colIndex, aState));
1265 #endif
1266 
1267  nsresult rv;
1268 
1269  if (mMouseState && mMouseStateRow >= 0 && mMouseStateColumn) {
1271  if (mTreeBoxObject) {
1272  rv = mTreeBoxObject->InvalidateCell(mMouseStateRow, mMouseStateColumn);
1273  NS_ENSURE_SUCCESS(rv, rv);
1274  }
1275  }
1276 
1277  mMouseState = aState;
1278  mMouseStateRow = aRow;
1279  mMouseStateColumn = aColumn;
1280 
1281  if (mMouseStateRow >= 0 && mMouseStateColumn && mTreeBoxObject) {
1282  rv = mTreeBoxObject->InvalidateCell(mMouseStateRow, mMouseStateColumn);
1283  NS_ENSURE_SUCCESS(rv, rv);
1284  }
1285 
1286  return NS_OK;
1287 }
1288 
1289 NS_IMETHODIMP
1290 sbLocalDatabaseTreeView::GetSelectionIsAll(PRBool* aSelectionIsAll)
1291 {
1292  NS_ENSURE_ARG_POINTER(aSelectionIsAll);
1293  NS_ASSERTION(mManageSelection,
1294  "GetSelectionIsAll() called but we're not managing selection");
1295 
1296  *aSelectionIsAll = mSelectionIsAll;
1297  return NS_OK;
1298 }
1299 
1300 NS_IMETHODIMP
1301 sbLocalDatabaseTreeView::GetSelectedValues(nsIStringEnumerator** aValues)
1302 {
1303  NS_ENSURE_ARG_POINTER(aValues);
1304  NS_ASSERTION(mManageSelection,
1305  "GetSelectedValues() called but we're not managing selection");
1306  nsresult rv;
1307 
1308  if (mSelectionIsAll) {
1309  nsTArray<nsString> empty;
1310  nsCOMPtr<nsIStringEnumerator> enumerator =
1311  new sbTArrayStringEnumerator(&empty);
1312  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
1313 
1314  enumerator.forget(aValues);
1315  return NS_OK;
1316  }
1317 
1318  nsTArray<nsString> guids;
1319  rv = EnumerateSelection(SelectionListGuidsEnumeratorCallback, &guids);
1320  NS_ENSURE_SUCCESS(rv, rv);
1321 
1322  rv = mSelectionList.EnumerateRead(SB_SelectionListGuidsToTArrayCallback,
1323  &guids);
1324  NS_ENSURE_SUCCESS(rv, rv);
1325 
1326  PRUint32 length = guids.Length();
1327  nsTArray<nsString> values(length);
1328  for (PRUint32 i = 0; i < length; i++) {
1329 
1330  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
1331  rv = GetBag(guids[i], getter_AddRefs(bag));
1332  NS_ENSURE_SUCCESS(rv, rv);
1333 
1334  nsString value;
1335  rv = bag->GetProperty(mCurrentSortProperty, value);
1336  NS_ENSURE_SUCCESS(rv, rv);
1337 
1338  nsString* appended = values.AppendElement(value);
1339  NS_ENSURE_TRUE(appended, NS_ERROR_OUT_OF_MEMORY);
1340  }
1341 
1342  nsCOMPtr<nsIStringEnumerator> enumerator =
1343  new sbTArrayStringEnumerator(&values);
1344  NS_ENSURE_TRUE(enumerator, NS_ERROR_OUT_OF_MEMORY);
1345 
1346  enumerator.forget(aValues);
1347  return NS_OK;
1348 }
1349 
1350 // sbILocalDatabaseGUIDArrayListener
1351 NS_IMETHODIMP
1352 sbLocalDatabaseTreeView::OnBeforeInvalidate(PRBool aInvalidateLength)
1353 {
1354  // array modified so reset everything
1355  mGuidWorkArray.Reset();
1356  mLastCachedRow = mFirstCachedRow = NOT_SET;
1357 
1358  if (mManageSelection) {
1359  nsresult rv = SaveSelectionList();
1360  NS_ENSURE_SUCCESS(rv, rv);
1361  }
1362 
1363  return NS_OK;
1364 }
1365 
1366 NS_IMETHODIMP
1367 sbLocalDatabaseTreeView::OnAfterInvalidate()
1368 {
1369  nsresult rv;
1370 
1371  rv = Rebuild();
1372  NS_ENSURE_SUCCESS(rv, rv);
1373 
1374  return NS_OK;
1375 }
1376 
1377 nsresult
1378 sbLocalDatabaseTreeView::GetPropertyForTreeColumn(nsITreeColumn* aTreeColumn,
1379  nsAString& aProperty)
1380 {
1381  NS_ENSURE_ARG_POINTER(aTreeColumn);
1382 
1383  nsresult rv;
1384 
1385  nsCOMPtr<nsIDOMElement> element;
1386  rv = aTreeColumn->GetElement(getter_AddRefs(element));
1387  NS_ENSURE_SUCCESS(rv, rv);
1388 
1389  rv = element->GetAttribute(NS_LITERAL_STRING("bind"), aProperty);
1390  NS_ENSURE_SUCCESS(rv, rv);
1391 
1392  return NS_OK;
1393 }
1394 
1395 nsresult
1396 sbLocalDatabaseTreeView::GetTreeColumnForProperty(const nsAString& aProperty,
1397  nsITreeColumn** aTreeColumn)
1398 {
1399  NS_ENSURE_ARG_POINTER(aTreeColumn);
1400  NS_ENSURE_STATE(mTreeBoxObject);
1401 
1402  nsCOMPtr<nsITreeColumns> columns;
1403  nsresult rv = mTreeBoxObject->GetColumns(getter_AddRefs(columns));
1404  NS_ENSURE_SUCCESS(rv, rv);
1405 
1406  PRInt32 count;
1407  rv = columns->GetCount(&count);
1408  NS_ENSURE_SUCCESS(rv, rv);
1409 
1410  for (PRInt32 i = 0; i < count; i++) {
1411  nsCOMPtr<nsITreeColumn> column;
1412  rv = columns->GetColumnAt(i, getter_AddRefs(column));
1413  NS_ENSURE_SUCCESS(rv, rv);
1414 
1415  nsCOMPtr<nsIDOMElement> element;
1416  rv = column->GetElement(getter_AddRefs(element));
1417  NS_ENSURE_SUCCESS(rv, rv);
1418 
1419  nsString bind;
1420  rv = element->GetAttribute(NS_LITERAL_STRING("bind"), bind);
1421  NS_ENSURE_SUCCESS(rv, rv);
1422 
1423  if (bind.Equals(aProperty)) {
1424  column.forget(aTreeColumn);
1425  return NS_OK;
1426  }
1427  }
1428 
1429  return NS_ERROR_NOT_AVAILABLE;
1430 }
1431 
1432 /* inline */ nsresult
1433 sbLocalDatabaseTreeView::GetColumnPropertyInfo(nsITreeColumn* aColumn,
1434  sbIPropertyInfo** aPropertyInfo)
1435 {
1436  nsString propertyID;
1437  nsresult rv = GetPropertyForTreeColumn(aColumn, propertyID);
1438  NS_ENSURE_SUCCESS(rv, rv);
1439 
1440  rv = mPropMan->GetPropertyInfo(propertyID, aPropertyInfo);
1441  NS_ENSURE_SUCCESS(rv, rv);
1442 
1443  return NS_OK;
1444 }
1445 
1446 nsresult
1447 sbLocalDatabaseTreeView::GetPropertyInfoAndValue(PRInt32 aRow,
1448  nsITreeColumn* aColumn,
1449  nsAString& aValue,
1450  sbIPropertyInfo** aPropertyInfo)
1451 {
1452  NS_ASSERTION(aColumn, "aColumn is null");
1453  NS_ASSERTION(aPropertyInfo, "aPropertyInfo is null");
1454  nsresult rv;
1455 
1456  PRUint32 index = TreeToArray(aRow);
1457  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
1458  rv = GetBag(index, getter_AddRefs(bag));
1459  NS_ENSURE_SUCCESS(rv, rv);
1460 
1461  nsCOMPtr<sbIPropertyInfo> pi;
1462  rv = GetColumnPropertyInfo(aColumn, getter_AddRefs(pi));
1463  NS_ENSURE_SUCCESS(rv, rv);
1464 
1465  nsString propertyID;
1466  rv = pi->GetId(propertyID);
1467  NS_ENSURE_SUCCESS(rv, rv);
1468 
1469  nsString value;
1470  rv = bag->GetProperty(propertyID, aValue);
1471  NS_ENSURE_SUCCESS(rv, rv);
1472 
1473  pi.forget(aPropertyInfo);
1474  return NS_OK;
1475 }
1476 
1477 nsresult
1478 sbLocalDatabaseTreeView::GetPlayingProperty(PRUint32 aIndex,
1479  nsISupportsArray* properties)
1480 {
1481  NS_ASSERTION(properties, "properties is null");
1482 
1483  nsresult rv;
1484 
1486  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1487  // cause events to be fired, or use synchronous proxies, as you risk //
1488  // crashing in recursive painting/frame construction. //
1490 
1491  if (!mPlayingItemUID.IsEmpty()) {
1492  nsString uid;
1493  rv = GetUniqueIdForIndex(aIndex, uid);
1494  NS_ENSURE_SUCCESS(rv, rv);
1495 
1496  if (mPlayingItemUID.Equals(uid)) {
1497  rv = TokenizeProperties(NS_LITERAL_STRING("playing"), properties);
1498  NS_ENSURE_SUCCESS(rv, rv);
1499  }
1500  }
1501 
1502  return NS_OK;
1503 }
1504 
1505 /* This property lets us know if device content is not represented in the main
1506  * library. If it's set for an item on a device, we display an icon
1507  * to indicate that we think that content is only on the device */
1508 nsresult
1509 sbLocalDatabaseTreeView::GetOriginNotInMainLibraryProperty
1510  (PRUint32 aIndex, nsISupportsArray* properties)
1511 {
1512  NS_ASSERTION(properties, "properties is null");
1513 
1514  nsresult rv;
1515 
1517  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1518  // cause events to be fired, or use synchronous proxies, as you risk //
1519  // crashing in recursive painting/frame construction. //
1521 
1522  if (!mViewingDeviceContent) {
1523  /* We only care about originNotInMainLibrary for things on a device.
1524  * We aren't viewing a device now, so we can stop checking. */
1525  return NS_OK;
1526  }
1527 
1528  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
1529  rv = GetBag(aIndex, getter_AddRefs(bag));
1530  NS_ENSURE_SUCCESS(rv, rv);
1531 
1532  nsString originInMainLibrary;
1533  rv = bag->GetProperty
1534  (NS_LITERAL_STRING(SB_PROPERTY_ORIGIN_IS_IN_MAIN_LIBRARY),
1535  originInMainLibrary);
1536  NS_ENSURE_SUCCESS(rv, rv);
1537 
1538  // We indicate the tracks that are _not_ in the main library
1539  if (!originInMainLibrary.EqualsLiteral("1")) {
1540  rv = TokenizeProperties(NS_LITERAL_STRING("originNotInMainLibrary"), properties);
1541  NS_ENSURE_SUCCESS(rv, rv);
1542  }
1543 
1544  return NS_OK;
1545 }
1546 
1547 nsresult
1548 sbLocalDatabaseTreeView::GetItemDisabledStatus(PRUint32 aIndex,
1549  nsISupportsArray* properties)
1550 {
1551  NS_ASSERTION(properties, "properties is null");
1552 
1553  // Ideally we could just use the row properties to indicate that a row is
1554  // 'unavailable', however if we do that then we can only use the
1555  // -moz-tree-row pseudoelement and change the background and border of a row,
1556  // and not the text style. In order to select on -moz-tree-cell-text, all of
1557  // the cells must have the 'unavailable' css property. This method adds the
1558  // css property to the array if the item at the index has the 'unavailable'
1559  // library property.
1560 
1561  nsresult rv;
1562 
1564  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1565  // cause events to be fired, or use synchronous proxies, as you risk //
1566  // crashing in recursive painting/frame construction. //
1568 
1569  nsString guid;
1570  rv = mArray->GetGuidByIndex(aIndex, guid);
1571  NS_ENSURE_SUCCESS(rv, rv);
1572 
1573  nsCOMPtr<sbIMediaList> mediaList;
1574  rv = mMediaListView->GetMediaList(getter_AddRefs(mediaList));
1575  NS_ENSURE_SUCCESS(rv, rv);
1576 
1577  nsCOMPtr<sbILibrary> library;
1578  rv = mediaList->GetLibrary(getter_AddRefs(library));
1579  NS_ENSURE_SUCCESS(rv, rv);
1580 
1581  nsCOMPtr<sbIMediaItem> mediaItem;
1582  rv = library->GetMediaItem(guid, getter_AddRefs(mediaItem));
1583  NS_ENSURE_SUCCESS(rv, rv);
1584 
1585  nsCOMPtr<sbIMediaItemController> mediaItemController;
1586  rv = mediaItem->GetItemController(getter_AddRefs(mediaItemController));
1587  NS_ENSURE_SUCCESS(rv, rv);
1588 
1589  if (!mediaItemController)
1590  return NS_OK;
1591 
1592  PRBool itemDisabled;
1593  rv = mediaItemController->IsItemDisabled(mediaItem, &itemDisabled);
1594  NS_ENSURE_SUCCESS(rv, rv);
1595 
1596  if (itemDisabled) {
1597  rv = TokenizeProperties(NS_LITERAL_STRING("disabled"), properties);
1598  NS_ENSURE_SUCCESS(rv, rv);
1599  }
1600 
1601  return NS_OK;
1602 }
1603 
1604 nsresult
1605 sbLocalDatabaseTreeView::GetPlayQueueStatus(PRUint32 aIndex,
1606  nsISupportsArray* properties)
1607 {
1608  NS_ASSERTION(properties, "properties is null");
1609 
1610  nsresult rv;
1611 
1613  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1614  // cause events to be fired, or use synchronous proxies, as you risk //
1615  // crashing in recursive painting/frame construction. //
1617 
1618  if (aIndex < mPlayQueueIndex) {
1619  rv = TokenizeProperties(NS_LITERAL_STRING("playqueue-history"), properties);
1620  NS_ENSURE_SUCCESS(rv, rv);
1621  } else if (aIndex == mPlayQueueIndex) {
1622  rv = TokenizeProperties(NS_LITERAL_STRING("playqueue-current"), properties);
1623  NS_ENSURE_SUCCESS(rv, rv);
1624  }
1625 
1626  return NS_OK;
1627 }
1628 
1629 nsresult
1630 sbLocalDatabaseTreeView::GetIsListReadOnly(PRBool *aOutIsReadOnly)
1631 {
1632  NS_ENSURE_ARG_POINTER(aOutIsReadOnly);
1633 
1634  nsresult rv;
1635 
1637  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1638  // cause events to be fired, or use synchronous proxies, as you risk //
1639  // crashing in recursive painting/frame construction. //
1641 
1642  nsCOMPtr<sbIMediaList> mediaList;
1643  rv = mMediaListView->GetMediaList(getter_AddRefs(mediaList));
1644  NS_ENSURE_SUCCESS(rv, rv);
1645 
1646  nsString isReadOnly;
1647  rv = mediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISREADONLY),
1648  isReadOnly);
1649  NS_ENSURE_SUCCESS(rv, rv);
1650 
1651  *aOutIsReadOnly = isReadOnly.EqualsLiteral("1");
1652  return NS_OK;
1653 }
1654 
1655 nsresult
1656 sbLocalDatabaseTreeView::GetBag(PRUint32 aIndex,
1658 {
1659  NS_ASSERTION(aBag, "aBag is null!");
1660  nsresult rv;
1661 
1662  nsString guid;
1663  rv = mArray->GetGuidByIndex(aIndex, guid);
1664  NS_ENSURE_SUCCESS(rv, rv);
1665 
1666  rv = GetBag(guid, aBag);
1667  NS_ENSURE_SUCCESS(rv, rv);
1668 
1669  return NS_OK;
1670 }
1671 
1672 nsresult
1673 sbLocalDatabaseTreeView::GetBag(const nsAString& aGuid,
1675 {
1676  NS_ASSERTION(aBag, "aBag is null!");
1677  nsresult rv;
1678 
1679  const PRUnichar* guidptr = aGuid.BeginReading();
1680 
1681  PRUint32 count = 0;
1682  sbILocalDatabaseResourcePropertyBag** bags = nsnull;
1683  rv = mPropertyCache->GetProperties(&guidptr, 1, &count, &bags);
1684  NS_ENSURE_SUCCESS(rv, rv);
1685 
1686  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
1687  if (count == 1 && bags[0]) {
1688  bag = bags[0];
1689  }
1690  NS_FREE_XPCOM_ISUPPORTS_POINTER_ARRAY(count, bags);
1691 
1692  if (!bag) {
1693  return NS_ERROR_NOT_AVAILABLE;
1694  }
1695 
1696  bag.forget(aBag);
1697  return NS_OK;
1698 }
1699 
1700 NS_IMETHODIMP
1701 sbLocalDatabaseTreeView::GetRowCount(PRInt32 *aRowCount)
1702 {
1703  NS_ENSURE_ARG_POINTER(aRowCount);
1704 
1706  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1707  // cause events to be fired, or use synchronous proxies, as you risk //
1708  // crashing in recursive painting/frame construction. //
1710 
1711  *aRowCount = mArrayLength + (mFakeAllRow ? 1 : 0);
1712  return NS_OK;
1713 }
1714 
1715 NS_IMETHODIMP
1716 sbLocalDatabaseTreeView::GetCellText(PRInt32 row,
1717  nsITreeColumn *col,
1718  nsAString& _retval)
1719 {
1720  NS_ENSURE_ARG_POINTER(col);
1721 
1722  nsresult rv;
1723 
1725  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1726  // cause events to be fired, or use synchronous proxies, as you risk //
1727  // crashing in recursive painting/frame construction. //
1729 
1730  if (IsAllRow(row)) {
1731  _retval.Assign(mLocalizedAll);
1732  return NS_OK;
1733  }
1734 
1735  rv = GetCellPropertyValue(TreeToArray(row), col, _retval);
1736  NS_ENSURE_SUCCESS(rv, rv);
1737 
1738  return NS_OK;
1739 }
1740 
1741 NS_IMETHODIMP
1742 sbLocalDatabaseTreeView::GetSelection(nsITreeSelection** aSelection)
1743 {
1744  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - GetSelection()", this));
1745 
1746  // this matches the MediaListView
1747  NS_ENSURE_ARG_POINTER(aSelection);
1748  NS_IF_ADDREF(*aSelection = mSelection);
1749  return NS_OK;
1750 
1751 }
1752 NS_IMETHODIMP
1753 sbLocalDatabaseTreeView::SetSelection(nsITreeSelection* aSelection)
1754 {
1755  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetSelection(0x%.8x)", this, aSelection));
1756 
1757  NS_ENSURE_ARG_POINTER(aSelection);
1758 
1759  // Wrap the selection given to us with our own proxy implementation so
1760  // we can get useful notifications on how the selection is changing
1761  if (mListType == eDistinct) {
1762  mSelection = new sbFilterTreeSelection(aSelection, this);
1763  NS_ENSURE_TRUE(mSelection, NS_ERROR_OUT_OF_MEMORY);
1764  }
1765  else {
1766  NS_ASSERTION(mViewSelection, "SetSelection called but no mViewSelection");
1767  mSelection = new sbPlaylistTreeSelection(aSelection, mViewSelection, this);
1768  NS_ENSURE_TRUE(mSelection, NS_ERROR_OUT_OF_MEMORY);
1769  }
1770 
1771  // Keep a ref to the real selection so we can modify it without going
1772  // through our proxy
1773  mRealSelection = aSelection;
1774 
1775  // If we're managing the selection, restore it
1776  if (mManageSelection) {
1777  RestoreSelection();
1778  }
1779 
1780  return NS_OK;
1781 }
1782 
1783 NS_IMETHODIMP
1784 sbLocalDatabaseTreeView::CycleHeader(nsITreeColumn* col)
1785 {
1786  NS_ENSURE_ARG_POINTER(col);
1787  nsresult rv;
1788 
1789  nsCOMPtr<sbIMediaList> mediaList;
1790  rv = mMediaListView->GetMediaList(getter_AddRefs(mediaList));
1791  NS_ENSURE_SUCCESS(rv, rv);
1792 
1793  // If the list is not sortable, ignore the click
1794  nsString isSortable;
1795  rv = mediaList->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ISSORTABLE),
1796  isSortable);
1797  NS_ENSURE_SUCCESS(rv, rv);
1798  if (isSortable.EqualsLiteral("0")) {
1799  return NS_OK;
1800  }
1801 
1802  nsString bind;
1803  rv = GetPropertyForTreeColumn(col, bind);
1804  NS_ENSURE_SUCCESS(rv, rv);
1805 
1806  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - CycleHeader %s", this,
1807  NS_LossyConvertUTF16toASCII(bind).get()));
1808 
1809  PRBool directionIsAscending = PR_TRUE;
1810  if (bind.Equals(mCurrentSortProperty)) {
1811  directionIsAscending = !mCurrentSortDirectionIsAscending;
1812  }
1813 
1814  rv = SetSort(bind, directionIsAscending);
1815  NS_ENSURE_SUCCESS(rv, rv);
1816 
1817  if (mObserver) {
1818  nsCOMPtr<sbIMediaListViewTreeViewObserver> observer =
1819  do_QueryReferent(mObserver);
1820  if (observer) {
1821  rv = observer->CycleHeader(col);
1822  NS_ENSURE_SUCCESS(rv, rv);
1823  }
1824  }
1825 
1826  return NS_OK;
1827 }
1828 
1829 NS_IMETHODIMP
1830 sbLocalDatabaseTreeView::CycleCell(PRInt32 row,
1831  nsITreeColumn* col)
1832 {
1833  NS_ENSURE_ARG_POINTER(col);
1834 
1835  return NS_ERROR_NOT_IMPLEMENTED;
1836 }
1837 
1838 NS_IMETHODIMP
1839 sbLocalDatabaseTreeView::GetRowProperties(PRInt32 row,
1840  nsISupportsArray* properties)
1841 {
1842  NS_ENSURE_ARG_MIN(row, 0);
1843  NS_ENSURE_ARG_POINTER(properties);
1844 
1845  nsresult rv;
1846 
1847  PRUint32 index = TreeToArray(row);
1848 
1850  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1851  // cause events to be fired, or use synchronous proxies, as you risk //
1852  // crashing in recursive painting/frame construction. //
1854 
1855  PRUint32 count;
1856  properties->Count(&count);
1857  nsString props;
1858  for (PRUint32 i = 0; i < count; i++) {
1859  nsCOMPtr<nsIAtom> atom;
1860  properties->QueryElementAt(i, NS_GET_IID(nsIAtom), getter_AddRefs(atom));
1861  if (atom) {
1862  nsString s;
1863  atom->ToString(s);
1864  props.Append(s);
1865  props.AppendLiteral(" ");
1866  }
1867  }
1868 
1869  if (IsAllRow(row)) {
1870  rv = TokenizeProperties(NS_LITERAL_STRING("all"), properties);
1871  NS_ENSURE_SUCCESS(rv, rv);
1872  return NS_OK;
1873  }
1874 
1875  rv = GetPlayingProperty(index, properties);
1876  NS_ENSURE_SUCCESS(rv, rv);
1877 
1878  rv = GetOriginNotInMainLibraryProperty(index, properties);
1879  NS_ENSURE_SUCCESS(rv, rv);
1880 
1881  rv = GetItemDisabledStatus(index, properties);
1882  NS_ENSURE_SUCCESS(rv, rv);
1883 
1884  if (mPlayQueueService) {
1885  rv = GetPlayQueueStatus(index, properties);
1886  NS_ENSURE_SUCCESS(rv, rv);
1887  }
1888 
1889  nsCOMPtr<sbILocalDatabaseResourcePropertyBag> bag;
1890  rv = GetBag(index, getter_AddRefs(bag));
1891  NS_ENSURE_SUCCESS(rv, rv);
1892 
1893  nsCOMPtr<nsIStringEnumerator> propertyEnumerator;
1894  rv = mPropMan->GetPropertyIDs(getter_AddRefs(propertyEnumerator));
1895  NS_ENSURE_SUCCESS(rv, rv);
1896 
1897  nsString propertyID;
1898  while (NS_SUCCEEDED(propertyEnumerator->GetNext(propertyID))) {
1899 
1900  nsString value;
1901  nsresult rv = bag->GetProperty(propertyID, value);
1902  NS_ENSURE_SUCCESS(rv, rv);
1903 
1904  nsCOMPtr<sbIPropertyInfo> propInfo;
1905  rv = mPropMan->GetPropertyInfo(propertyID, getter_AddRefs(propInfo));
1906  NS_ENSURE_SUCCESS(rv, rv);
1907 
1908  nsCOMPtr<sbITreeViewPropertyInfo> tvpi = do_QueryInterface(propInfo, &rv);
1909  if (NS_SUCCEEDED(rv)) {
1910  nsString propertiesString;
1911  rv = tvpi->GetRowProperties(value, propertiesString);
1912  NS_ENSURE_SUCCESS(rv, rv);
1913 
1914  if (!propertiesString.IsEmpty()) {
1915  rv = TokenizeProperties(propertiesString, properties);
1916  NS_ENSURE_SUCCESS(rv, rv);
1917  }
1918  }
1919  }
1920 
1921  return NS_OK;
1922 }
1923 
1924 NS_IMETHODIMP
1925 sbLocalDatabaseTreeView::GetCellProperties(PRInt32 row,
1926  nsITreeColumn* col,
1927  nsISupportsArray* properties)
1928 {
1929  NS_ENSURE_ARG_MIN(row, 0);
1930  NS_ENSURE_ARG_POINTER(col);
1931  NS_ENSURE_ARG_POINTER(properties);
1932 
1934  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
1935  // cause events to be fired, or use synchronous proxies, as you risk //
1936  // crashing in recursive painting/frame construction. //
1938 
1939 #ifdef PR_LOGGING
1940  PRInt32 colIndex = -1;
1941  col->GetIndex(&colIndex);
1942 
1943  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - GetCellProperties(%d, %d)", this,
1944  row, colIndex));
1945 #endif
1946 
1947  if (IsAllRow(row)) {
1948  return NS_OK;
1949  }
1950 
1951  nsresult rv;
1952 
1953  rv = GetColumnProperties(col, properties);
1954  NS_ENSURE_SUCCESS(rv, rv);
1955 
1956  // Add the mouse states
1957  if (mMouseStateRow == row && mMouseStateColumn == col) {
1958  switch(mMouseState) {
1960  rv = TokenizeProperties(NS_LITERAL_STRING("cell-hover"), properties);
1961  NS_ENSURE_SUCCESS(rv, rv);
1962  break;
1964  rv = TokenizeProperties(NS_LITERAL_STRING("cell-active"), properties);
1965  NS_ENSURE_SUCCESS(rv, rv);
1966  break;
1967  }
1968  }
1969 
1970  PRUint32 index = TreeToArray(row);
1971 
1972  rv = GetPlayingProperty(index, properties);
1973  NS_ENSURE_SUCCESS(rv, rv);
1974 
1975  rv = GetOriginNotInMainLibraryProperty(index, properties);
1976  NS_ENSURE_SUCCESS(rv, rv);
1977 
1978  rv = GetItemDisabledStatus(index, properties);
1979  NS_ENSURE_SUCCESS(rv, rv);
1980 
1981  if (mPlayQueueService) {
1982  rv = GetPlayQueueStatus(index, properties);
1983  NS_ENSURE_SUCCESS(rv, rv);
1984  }
1985 
1986  nsCOMPtr<sbIPropertyInfo> pi;
1987  nsString value;
1988  rv = GetPropertyInfoAndValue(row, col, value, getter_AddRefs(pi));
1989  NS_ENSURE_SUCCESS(rv, rv);
1990 
1991  nsCOMPtr<sbITreeViewPropertyInfo> tvpi = do_QueryInterface(pi, &rv);
1992  if (NS_SUCCEEDED(rv)) {
1993  nsString propertiesString;
1994  rv = tvpi->GetCellProperties(value, propertiesString);
1995  NS_ENSURE_SUCCESS(rv, rv);
1996 
1997  if (!propertiesString.IsEmpty()) {
1998  rv = TokenizeProperties(propertiesString, properties);
1999  NS_ENSURE_SUCCESS(rv, rv);
2000  }
2001  }
2002 
2003  nsCOMPtr<sbIClickablePropertyInfo> cpi = do_QueryInterface(pi, &rv);
2004  if (NS_SUCCEEDED(rv)) {
2005  PRBool isDisabled;
2006  rv = cpi->IsDisabled(value, &isDisabled);
2007  NS_ENSURE_SUCCESS(rv, rv);
2008 
2009  if (isDisabled) {
2010  rv = TokenizeProperties(NS_LITERAL_STRING("disabled"), properties);
2011  NS_ENSURE_SUCCESS(rv, rv);
2012  }
2013  }
2014 
2015  // If the current medialist is readonly, be set the "readonly" property.
2016  PRBool isMediaListReadOnly;
2017  rv = GetIsListReadOnly(&isMediaListReadOnly);
2018  if (NS_SUCCEEDED(rv) && isMediaListReadOnly) {
2019  rv = TokenizeProperties(NS_LITERAL_STRING("readonly"), properties);
2020  NS_ENSURE_SUCCESS(rv, rv);
2021  }
2022 
2023  return NS_OK;
2024 }
2025 
2026 NS_IMETHODIMP
2027 sbLocalDatabaseTreeView::GetColumnProperties(nsITreeColumn* col,
2028  nsISupportsArray* properties)
2029 {
2030  NS_ENSURE_ARG_POINTER(col);
2031  NS_ENSURE_ARG_POINTER(properties);
2032 
2033  nsString propertyID;
2034  nsresult rv = GetPropertyForTreeColumn(col, propertyID);
2035  NS_ENSURE_SUCCESS(rv, rv);
2036 
2037  // Turn the property name into something that CSS can handle. For example
2038  //
2039  // http://songbirdnest.com/data/1.0#rating
2040  //
2041  // becomes:
2042  //
2043  // http-songbirdnest-com-data-1-0-rating
2044 
2045  NS_NAMED_LITERAL_STRING(badChars, BAD_CSS_CHARS);
2046  static const PRUnichar kHyphenChar = '-';
2047 
2048  for (PRUint32 index = 0; index < propertyID.Length(); index++) {
2049  PRUnichar testChar = propertyID.CharAt(index);
2050 
2051  // Short circuit for ASCII alphanumerics.
2052  if ((testChar >= 97 && testChar <= 122) || // a-z
2053  (testChar >= 65 && testChar <= 90) || // A-Z
2054  (testChar >= 48 && testChar <= 57)) { // 0-9
2055  continue;
2056  }
2057 
2058  PRInt32 badCharIndex= badChars.FindChar(testChar);
2059  if (badCharIndex > -1) {
2060  if (index > 0 && propertyID.CharAt(index - 1) == kHyphenChar) {
2061  propertyID.Replace(index, 1, nsnull, 0);
2062  index--;
2063  }
2064  else {
2065  propertyID.Replace(index, 1, kHyphenChar);
2066  }
2067  }
2068  }
2069 
2070  rv = TokenizeProperties(propertyID, properties);
2071  NS_ENSURE_SUCCESS(rv, rv);
2072 
2073  return NS_OK;
2074 }
2075 
2076 NS_IMETHODIMP
2077 sbLocalDatabaseTreeView::IsContainer(PRInt32 row, PRBool* _retval)
2078 {
2079  NS_ENSURE_ARG_POINTER(_retval);
2080 
2081  *_retval = PR_FALSE;
2082 
2083  return NS_OK;
2084 }
2085 
2086 NS_IMETHODIMP
2087 sbLocalDatabaseTreeView::IsContainerOpen(PRInt32 row, PRBool* _retval)
2088 {
2089  NS_ENSURE_ARG_POINTER(_retval);
2090 
2091  *_retval = PR_FALSE;
2092 
2093  return NS_OK;
2094 }
2095 
2096 NS_IMETHODIMP
2097 sbLocalDatabaseTreeView::IsContainerEmpty(PRInt32 row, PRBool* _retval)
2098 {
2099  NS_ENSURE_ARG_POINTER(_retval);
2100 
2101  *_retval = PR_TRUE;
2102 
2103  return NS_OK;
2104 }
2105 
2106 NS_IMETHODIMP
2107 sbLocalDatabaseTreeView::IsSeparator(PRInt32 row, PRBool* _retval)
2108 {
2109  NS_ENSURE_ARG_POINTER(_retval);
2110 
2111  *_retval = PR_FALSE;
2112 
2113  return NS_OK;
2114 }
2115 
2116 NS_IMETHODIMP
2117 sbLocalDatabaseTreeView::IsSorted(PRBool* _retval)
2118 {
2119  NS_ENSURE_ARG_POINTER(_retval);
2120 
2121  // Media lists are always sorted
2122  *_retval = PR_TRUE;
2123  return NS_OK;
2124 }
2125 
2126 NS_IMETHODIMP
2127 sbLocalDatabaseTreeView::CanDrop(PRInt32 row,
2128  PRInt32 orientation,
2129  nsIDOMDataTransfer* dataTransfer,
2130  PRBool* _retval)
2131 {
2132  NS_ENSURE_ARG_POINTER(_retval);
2133 
2134  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - CanDrop(%d, %d)", this,
2135  row, orientation));
2136 
2137  if (!IsAllRow(row) && mObserver) {
2138 
2139  nsCOMPtr<sbIMediaListViewTreeViewObserver> observer =
2140  do_QueryReferent(mObserver);
2141  if (observer) {
2142  nsresult rv = observer->CanDrop(TreeToArray(row),
2143  orientation,
2144  dataTransfer,
2145  _retval);
2146  NS_ENSURE_SUCCESS(rv, rv);
2147  }
2148  }
2149  else {
2150  *_retval = PR_FALSE;
2151  }
2152 
2153  return NS_OK;
2154 }
2155 
2156 NS_IMETHODIMP
2157 sbLocalDatabaseTreeView::Drop(PRInt32 row,
2158  PRInt32 orientation,
2159  nsIDOMDataTransfer* dataTransfer)
2160 {
2161  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - Drop(%d, %d)", this,
2162  row, orientation));
2163 
2164  if (!IsAllRow(row) && mObserver) {
2165 
2166  nsCOMPtr<sbIMediaListViewTreeViewObserver> observer =
2167  do_QueryReferent(mObserver);
2168  if (observer) {
2169  nsresult rv = observer->Drop(TreeToArray(row), orientation, dataTransfer);
2170  NS_ENSURE_SUCCESS(rv, rv);
2171  }
2172  }
2173 
2174  return NS_OK;
2175 }
2176 
2177 NS_IMETHODIMP
2178 sbLocalDatabaseTreeView::GetParentIndex(PRInt32 rowIndex, PRInt32* _retval)
2179 {
2180  NS_ENSURE_ARG_POINTER(_retval);
2181 
2182  // we never have parent indices, return -1 as specified in the IDL
2183  *_retval = -1;
2184  return NS_OK;
2185 }
2186 
2187 NS_IMETHODIMP
2188 sbLocalDatabaseTreeView::HasNextSibling(PRInt32 rowIndex,
2189  PRInt32 afterIndex,
2190  PRBool* _retval)
2191 {
2192  NS_ENSURE_ARG_POINTER(_retval);
2193 
2194  return NS_ERROR_NOT_IMPLEMENTED;
2195 }
2196 
2197 NS_IMETHODIMP
2198 sbLocalDatabaseTreeView::GetLevel(PRInt32 row, PRInt32* _retval)
2199 {
2200  NS_ENSURE_ARG_POINTER(_retval);
2201 
2202  *_retval = 0;
2203  return NS_OK;
2204 }
2205 
2206 NS_IMETHODIMP
2207 sbLocalDatabaseTreeView::GetImageSrc(PRInt32 row,
2208  nsITreeColumn* col,
2209  nsAString& _retval)
2210 {
2211  NS_ENSURE_ARG_POINTER(col);
2212 
2213  if (IsAllRow(row)) {
2214  return NS_OK;
2215  }
2216 
2217  nsresult rv;
2218 
2219  nsString value;
2220  nsCOMPtr<sbIPropertyInfo> pi;
2221  rv = GetPropertyInfoAndValue(row, col, value, getter_AddRefs(pi));
2222  NS_ENSURE_SUCCESS(rv, rv);
2223 
2224  nsCOMPtr<sbITreeViewPropertyInfo> tvpi = do_QueryInterface(pi, &rv);
2225  if (NS_SUCCEEDED(rv)) {
2226  rv = tvpi->GetImageSrc(value, _retval);
2227  NS_ENSURE_SUCCESS(rv, rv);
2228  }
2229 
2230  return NS_OK;
2231 }
2232 
2233 NS_IMETHODIMP
2234 sbLocalDatabaseTreeView::GetProgressMode(PRInt32 row,
2235  nsITreeColumn* col,
2236  PRInt32* _retval)
2237 {
2238  NS_ENSURE_ARG_POINTER(col);
2239  NS_ENSURE_ARG_POINTER(_retval);
2240 
2241  if (IsAllRow(row)) {
2242  *_retval = nsITreeView::PROGRESS_NONE;
2243  return NS_OK;
2244  }
2245 
2246  nsresult rv;
2247 
2248  nsString value;
2249  nsCOMPtr<sbIPropertyInfo> pi;
2250  rv = GetPropertyInfoAndValue(row, col, value, getter_AddRefs(pi));
2251  NS_ENSURE_SUCCESS(rv, rv);
2252 
2253  nsCOMPtr<sbITreeViewPropertyInfo> tvpi = do_QueryInterface(pi, &rv);
2254  if (NS_SUCCEEDED(rv)) {
2255  rv = tvpi->GetProgressMode(value, _retval);
2256  NS_ENSURE_SUCCESS(rv, rv);
2257  }
2258  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - GetMode(%d, %d) = %d", this,
2259  row, col, _retval));
2260 
2261  return NS_OK;
2262 }
2263 
2264 NS_IMETHODIMP
2265 sbLocalDatabaseTreeView::GetCellValue(PRInt32 row,
2266  nsITreeColumn* col,
2267  nsAString& _retval)
2268 {
2269 #ifdef PR_LOGGING
2270  PRInt32 colIndex = -1;
2271  col->GetIndex(&colIndex);
2272 
2273  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - GetCellValue(%d, %d)", this,
2274  row, colIndex));
2275 #endif
2276 
2278  // WARNING: This method is called during Paint. DO NOT MODIFY THE TREE, //
2279  // cause events to be fired, or use synchronous proxies, as you risk //
2280  // crashing in recursive painting/frame construction. //
2282 
2283  if (IsAllRow(row)) {
2284  _retval.Truncate();
2285  return NS_OK;
2286  }
2287 
2288  nsresult rv;
2289 
2290  nsCOMPtr<sbIPropertyInfo> pi;
2291  nsString value;
2292  rv = GetPropertyInfoAndValue(row, col, value, getter_AddRefs(pi));
2293  NS_ENSURE_SUCCESS(rv, rv);
2294 
2295  nsCOMPtr<sbITreeViewPropertyInfo> tvpi = do_QueryInterface(pi, &rv);
2296  if (NS_SUCCEEDED(rv)) {
2297  rv = tvpi->GetCellValue(value, _retval);
2298  NS_ENSURE_SUCCESS(rv, rv);
2299  }
2300 
2301  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - GetCellValue(%d, %d) = %s", this,
2302  row, colIndex, NS_LossyConvertUTF16toASCII(_retval).get()));
2303 
2304  return NS_OK;
2305 }
2306 
2307 NS_IMETHODIMP
2308 sbLocalDatabaseTreeView::SetTree(nsITreeBoxObject *tree)
2309 {
2310  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetTree(0x%.8x)", this, tree));
2311  nsresult rv;
2312 
2313  // XXX: nsTreeBoxObject calls this method with a null to break a cycle so
2314  // we can't NS_ENSURE_ARG_POINTER(tree)
2315 
2316  mTreeBoxObject = tree;
2317 
2318  if (tree) {
2319  nsresult rv = UpdateColumnSortAttributes(mCurrentSortProperty,
2320  mCurrentSortDirectionIsAscending);
2321  NS_ENSURE_SUCCESS(rv, rv);
2322 
2323  // Rebuild view with the new tree
2324  rv = Rebuild();
2325  NS_ENSURE_SUCCESS(rv, rv);
2326  }
2327  // If we are detaching from the tree, save
2328  // the selection so that it can be
2329  // restored if we are ever rebound.
2330  else if (mManageSelection) {
2331  nsresult rv = SaveSelectionList();
2332  NS_ENSURE_SUCCESS(rv, rv);
2333  }
2334 
2335  // Manage our listener to the playback service. Attach the listener when
2336  // we are bound to a tree and remove it when we are unbound. We do this
2337  // because we need to remove our listener when we go away, but we can't do
2338  // it in the destructor because we wind up creating a wrapper that eventually
2339  // points to a deleted this.
2340  if (mMediacoreManager) {
2341  nsCOMPtr<sbIMediacoreEventListener> eventListener =
2342  do_QueryInterface(NS_ISUPPORTS_CAST(sbIMediacoreEventListener*, this), &rv);
2343  NS_ENSURE_SUCCESS(rv, rv);
2344 
2345  nsCOMPtr<sbIMediacoreEventTarget> target =
2346  do_QueryReferent(mMediacoreManager, &rv);
2347  NS_ENSURE_SUCCESS(rv, rv);
2348 
2349  if (tree) {
2350  if (!mIsListeningToPlayback) {
2351 
2352  rv = target->AddListener(eventListener);
2353  NS_ENSURE_SUCCESS(rv, rv);
2354  mIsListeningToPlayback = PR_TRUE;
2355 
2356  // Initialize the playing indicator
2357  rv = OnTrackChange();
2358  NS_ENSURE_SUCCESS(rv, rv);
2359  }
2360  else {
2361  NS_WARNING("Tree set but we're already listening to playback!");
2362  }
2363  }
2364  else {
2365  if (mIsListeningToPlayback) {
2366  rv = target->RemoveListener(eventListener);
2367  NS_ENSURE_SUCCESS(rv, rv);
2368  mIsListeningToPlayback = PR_FALSE;
2369  }
2370  else {
2371  NS_WARNING("Tree nulled but we're not listening to playback!");
2372  }
2373  }
2374  }
2375 
2376  return NS_OK;
2377 }
2378 
2379 NS_IMETHODIMP
2380 sbLocalDatabaseTreeView::ToggleOpenState(PRInt32 row)
2381 {
2382  return NS_ERROR_NOT_IMPLEMENTED;
2383 }
2384 
2385 NS_IMETHODIMP
2386 sbLocalDatabaseTreeView::SelectionChanged()
2387 {
2388  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SelectionChanged()", this));
2389 
2390  return NS_OK;
2391 }
2392 
2393 NS_IMETHODIMP
2394 sbLocalDatabaseTreeView::IsEditable(PRInt32 row,
2395  nsITreeColumn* col,
2396  PRBool* _retval)
2397 {
2398  NS_ENSURE_ARG_POINTER(col);
2399  NS_ENSURE_ARG_POINTER(_retval);
2400 
2401  if (IsAllRow(row)) {
2402  *_retval = PR_FALSE;
2403  return NS_OK;
2404  }
2405 
2406  nsresult rv;
2407 
2408  nsCOMPtr<sbIPropertyInfo> propInfo;
2409  rv = GetColumnPropertyInfo(col, getter_AddRefs(propInfo));
2410  NS_ENSURE_SUCCESS(rv, rv);
2411 
2412  rv = propInfo->GetUserEditable(_retval);
2413  NS_ENSURE_SUCCESS(rv, rv);
2414 
2415  return NS_OK;
2416 }
2417 
2418 NS_IMETHODIMP
2419 sbLocalDatabaseTreeView::IsSelectable(PRInt32 row,
2420  nsITreeColumn* col,
2421  PRBool* _retval)
2422 {
2423  NS_ENSURE_ARG_POINTER(col);
2424  NS_ENSURE_ARG_POINTER(_retval);
2425 
2426  return NS_ERROR_NOT_IMPLEMENTED;
2427 }
2428 
2429 NS_IMETHODIMP
2430 sbLocalDatabaseTreeView::SetCellValue(PRInt32 row,
2431  nsITreeColumn* col,
2432  const nsAString& value)
2433 {
2434  NS_ENSURE_ARG_POINTER(col);
2435 
2436  nsresult rv;
2437 #ifdef PR_LOGGING
2438  PRInt32 colIndex;
2439  rv = col->GetIndex(&colIndex);
2440  NS_ENSURE_SUCCESS(rv, rv);
2441 
2442  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetCellValue(%d, %d %s)", this,
2443  row, colIndex, NS_LossyConvertUTF16toASCII(value).get()));
2444 #endif
2445  rv = SetCellText(row, col, value);
2446  NS_ENSURE_SUCCESS(rv, rv);
2447 
2448  return NS_OK;
2449 }
2450 
2451 NS_IMETHODIMP
2452 sbLocalDatabaseTreeView::SetCellText(PRInt32 row,
2453  nsITreeColumn* col,
2454  const nsAString& value)
2455 {
2456  NS_ENSURE_ARG_POINTER(col);
2457  NS_ENSURE_STATE(mMediaListView);
2458 
2459  if (IsAllRow(row)) {
2460  return NS_OK;
2461  }
2462 
2463  nsresult rv;
2464 #ifdef PR_LOGGING
2465  PRInt32 colIndex;
2466  rv = col->GetIndex(&colIndex);
2467  NS_ENSURE_SUCCESS(rv, rv);
2468 
2469  TRACE(("sbLocalDatabaseTreeView[0x%.8x] - SetCellText(%d, %d %s)", this,
2470  row, colIndex, NS_LossyConvertUTF16toASCII(value).get()));
2471 #endif
2472 
2473  nsCOMPtr<sbIPropertyInfo> info;
2474  rv = GetColumnPropertyInfo(col, getter_AddRefs(info));
2475  NS_ENSURE_SUCCESS(rv, rv);
2476 
2477  nsString bind;
2478  rv = GetPropertyForTreeColumn(col, bind);
2479  NS_ENSURE_SUCCESS(rv, rv);
2480 
2481  nsString guid;
2482  rv = mArray->GetGuidByIndex(TreeToArray(row), guid);
2483  NS_ENSURE_SUCCESS(rv, rv);
2484 
2485  nsCOMPtr<sbIMediaList> mediaList;
2486  rv = mMediaListView->GetMediaList(getter_AddRefs(mediaList));
2487  NS_ENSURE_SUCCESS(rv, rv);
2488 
2489  nsCOMPtr<sbILibrary> library;
2490  rv = mediaList->GetLibrary(getter_AddRefs(library));
2491  NS_ENSURE_SUCCESS(rv, rv);
2492 
2493  nsCOMPtr<sbIMediaItem> item;
2494  rv = library->GetMediaItem(guid, getter_AddRefs(item));
2495  NS_ENSURE_SUCCESS(rv, rv);
2496 
2497  nsString oldValue;
2498  rv = item->GetProperty(bind, oldValue);
2499  NS_ENSURE_SUCCESS(rv, rv);
2500 
2501  if (!value.Equals(oldValue)) {
2502  rv = item->SetProperty(bind, value);
2503  // We need to handle the ILLEGAL_VALUE case specially or the
2504  // tree.xml::stopEditing() errors out and leaves the inline
2505  // editor showing on deselect.
2506  if (rv == NS_ERROR_ILLEGAL_VALUE) {
2507  // We do not notify any observers that the cell has been edited
2508  // in the event of illegal data, however, we don't want to throw
2509  // an error exception, so we'll throw this sorta-success in case
2510  // anyone cares.
2511  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
2512  }
2513  NS_ENSURE_SUCCESS(rv, rv);
2514 
2515  if (mObserver) {
2516  nsCOMPtr<sbIMediaListViewTreeViewObserver> observer =
2517  do_QueryReferent(mObserver);
2518  if (observer) {
2519  rv = observer->OnCellEdited(item, bind, oldValue);
2520  NS_ENSURE_SUCCESS(rv, rv);
2521  }
2522  }
2523  }
2524 
2525  return NS_OK;
2526 }
2527 
2528 NS_IMETHODIMP
2529 sbLocalDatabaseTreeView::PerformAction(const PRUnichar* action)
2530 {
2531  NS_ENSURE_ARG_POINTER(action);
2532 
2533  return NS_ERROR_NOT_IMPLEMENTED;
2534 }
2535 
2536 NS_IMETHODIMP
2537 sbLocalDatabaseTreeView::PerformActionOnRow(const PRUnichar* action,
2538  PRInt32 row)
2539 {
2540  NS_ENSURE_ARG_POINTER(action);
2541 
2542  return NS_ERROR_NOT_IMPLEMENTED;
2543 }
2544 
2545 NS_IMETHODIMP
2546 sbLocalDatabaseTreeView::PerformActionOnCell(const PRUnichar* action,
2547  PRInt32 row,
2548  nsITreeColumn* col)
2549 {
2550  NS_ENSURE_ARG_POINTER(action);
2551  NS_ENSURE_ARG_POINTER(col);
2552 
2553  return NS_ERROR_NOT_IMPLEMENTED;
2554 }
2555 
2559 NS_IMETHODIMP
2560 sbLocalDatabaseTreeView::GetNextRowIndexForKeyNavigation(const nsAString& aKeyString,
2561  PRUint32 aStartFrom,
2562  PRInt32* _retval)
2563 {
2564  NS_ASSERTION(NS_IsMainThread(), "wrong thread");
2565  NS_ENSURE_FALSE(aKeyString.IsEmpty(), NS_ERROR_INVALID_ARG);
2566  NS_ENSURE_ARG_POINTER(_retval);
2567 
2568  nsString keyString(aKeyString);
2569  PRUint32 keyStringLength = keyString.Length();
2570 
2571  // Make sure that this is lowercased. The obj_searchable table should always be
2572  // lowercased.
2573  ToLowerCase(keyString);
2574 
2575  // Most folks will use this function to check the very next row in the tree.
2576  // Try that before doing the costly database search.
2577  nsString testString;
2578  nsresult rv = mArray->GetSortPropertyValueByIndex(aStartFrom, testString);
2579  NS_ENSURE_SUCCESS(rv, rv);
2580 
2581  if (!keyString.Compare(Substring(testString, 0, keyStringLength))) {
2582  *_retval = aStartFrom;
2583  return NS_OK;
2584  }
2585 
2586  PRUint32 index;
2587  rv = mArray->GetFirstIndexByPrefix(keyString, &index);
2588  if (NS_FAILED(rv) || (index < aStartFrom)) {
2589  *_retval = -1;
2590  return NS_OK;
2591  }
2592 
2593  // If we have a fake all row, we have to add 1 to the index we return.
2594  if (mFakeAllRow) {
2595  index++;
2596  }
2597 
2598  *_retval = (PRInt32)index;
2599  return NS_OK;
2600 }
2601 
2605 NS_IMETHODIMP
2606 sbLocalDatabaseTreeView::SetObserver(sbIMediaListViewTreeViewObserver* aObserver)
2607 {
2608  nsresult rv;
2609 
2610  if (aObserver) {
2611  mObserver = do_GetWeakReference(aObserver, &rv);;
2612  NS_ENSURE_SUCCESS(rv, rv);
2613  }
2614  else {
2615  mObserver = nsnull;
2616  }
2617 
2618  return NS_OK;
2619 }
2620 
2624 NS_IMETHODIMP
2625 sbLocalDatabaseTreeView::GetObserver(sbIMediaListViewTreeViewObserver** aObserver)
2626 {
2627  NS_ENSURE_ARG_POINTER(aObserver);
2628 
2629  *aObserver = nsnull;
2630  if (mObserver) {
2631  nsCOMPtr<sbIMediaListViewTreeViewObserver> observer =
2632  do_QueryReferent(mObserver);
2633  if (observer) {
2634  observer.forget(aObserver);
2635  }
2636  }
2637 
2638  return NS_OK;
2639 }
2640 
2641 // -----------------------------------------------------------------------------
2642 // sbIMediacoreEventListener
2643 // -----------------------------------------------------------------------------
2644 NS_IMETHODIMP
2645 sbLocalDatabaseTreeView::OnMediacoreEvent(sbIMediacoreEvent *aEvent)
2646 {
2647  NS_ENSURE_ARG_POINTER(aEvent);
2648 
2649  PRUint32 eventType = 0;
2650  nsresult rv = aEvent->GetType(&eventType);
2651  NS_ENSURE_SUCCESS(rv, rv);
2652 
2653  switch(eventType) {
2657  rv = OnTrackChange();
2658  NS_ENSURE_SUCCESS(rv, rv);
2659  }
2660  break;
2661 
2664  rv = OnStop();
2665  NS_ENSURE_SUCCESS(rv, rv);
2666  }
2667  break;
2668  }
2669 
2670  return NS_OK;
2671 }
2672 
2673 // -----------------------------------------------------------------------------
2674 // Event Listener Handlers
2675 // -----------------------------------------------------------------------------
2676 nsresult
2678 {
2679  nsresult rv;
2680 
2681  mPlayingItemUID = EmptyString();
2682 
2683  if (mTreeBoxObject) {
2684  rv = mTreeBoxObject->Invalidate();
2685  NS_ENSURE_SUCCESS(rv, rv);
2686  }
2687 
2688  return NS_OK;
2689 }
2690 
2691 nsresult
2693 {
2694  nsresult rv = NS_ERROR_UNEXPECTED;
2695 
2696  nsCOMPtr<sbIMediacoreManager> manager =
2697  do_QueryReferent(mMediacoreManager, &rv);
2698  NS_ENSURE_SUCCESS(rv, rv);
2699 
2700  nsCOMPtr<sbIMediacoreSequencer> sequencer;
2701  rv = manager->GetSequencer(getter_AddRefs(sequencer));
2702  NS_ENSURE_SUCCESS(rv, rv);
2703 
2704  nsCOMPtr<sbIMediaListView> playingView;
2705  rv = sequencer->GetView(getter_AddRefs(playingView));
2706  NS_ENSURE_SUCCESS(rv, rv);
2707 
2708  PRUint32 playingViewIndex = 0;
2709  rv = sequencer->GetViewPosition(&playingViewIndex);
2710  NS_ENSURE_SUCCESS(rv, rv);
2711 
2712  rv = OnTrackChange(playingView, playingViewIndex);
2713  NS_ENSURE_SUCCESS(rv, rv);
2714 
2715  return NS_OK;
2716 }
2717 
2718 nsresult
2720  PRUint32 aIndex)
2721 {
2722  nsresult rv;
2723 
2724  if (aView && mMediaListView) {
2725  nsCOMPtr<sbIMediaList> viewList;
2726  rv = mMediaListView->GetMediaList(getter_AddRefs(viewList));
2727  NS_ENSURE_SUCCESS(rv, rv);
2728 
2729  nsCOMPtr<sbIMediaList> playingList;
2730  rv = aView->GetMediaList(getter_AddRefs(playingList));
2731  NS_ENSURE_SUCCESS(rv, rv);
2732 
2733  PRBool equals;
2734  rv = viewList->Equals(playingList, &equals);
2735  NS_ENSURE_SUCCESS(rv, rv);
2736 
2737  nsCOMPtr<sbIMediacoreManager> manager =
2738  do_QueryReferent(mMediacoreManager, &rv);
2739  NS_ENSURE_SUCCESS(rv, rv);
2740 
2741  nsCOMPtr<sbIMediacoreStatus> status;
2742  rv = manager->GetStatus(getter_AddRefs(status));
2743  NS_ENSURE_SUCCESS(rv, rv);
2744 
2745  PRUint32 state = 0;
2746  rv = status->GetState(&state);
2747  NS_ENSURE_SUCCESS(rv, rv);
2748 
2749  PRBool isPlaying = (state == sbIMediacoreStatus::STATUS_PLAYING ||
2752 
2753  if (equals && isPlaying) {
2754  nsString uid;
2755  rv = aView->GetViewItemUIDForIndex(aIndex, uid);
2756  NS_ENSURE_SUCCESS(rv, rv);
2757 
2758  PRUint32 index;
2759  rv = mMediaListView->GetIndexForViewItemUID(uid, &index);
2760  if (NS_SUCCEEDED(rv)) {
2761  rv = GetUniqueIdForIndex(index, mPlayingItemUID);
2762  NS_ENSURE_SUCCESS(rv, rv);
2763  }
2764  else {
2765  mPlayingItemUID = EmptyString();
2766  }
2767  }
2768  else {
2769  mPlayingItemUID = EmptyString();
2770  }
2771  }
2772  else {
2773  mPlayingItemUID = EmptyString();
2774  }
2775 
2776  if (mTreeBoxObject) {
2777  rv = mTreeBoxObject->Invalidate();
2778  NS_ENSURE_SUCCESS(rv, rv);
2779  }
2780 
2781  return NS_OK;
2782 }
2783 
2784 nsresult
2786 {
2787  NS_ENSURE_ARG_POINTER(aEvent);
2788 
2789  // The index of the playing track has changed. This is either due to the
2790  // user moving the track around (in which case the UID of the item has not
2791  // changed) or to the whole content of the list being replaced (eg, a smart
2792  // playlist being rebuilt), in which case the currently playing item (if it
2793  // is still there) has a brand new UID. In both cases it is safe to just
2794  // forward the arguments to OnTrackChange in order to use the currently
2795  // playing item's UID.
2796  nsresult rv = OnTrackChange();
2797  NS_ENSURE_SUCCESS(rv, rv);
2798 
2799  return NS_OK;
2800 }
2801 
2802 // sbIMediaListViewSelectionListener
2803 NS_IMETHODIMP
2804 sbLocalDatabaseTreeView::OnSelectionChanged()
2805 {
2806  LOG(("sbLocalDatabaseTreeView[0x%.8x] - OnSelectionChanged", this));
2807  if (mListType != eDistinct && mTreeBoxObject && mRealSelection) {
2808  // Current index may have changed as well
2809  OnCurrentIndexChanged();
2810  nsresult rv = mTreeBoxObject->Invalidate();
2811  NS_ENSURE_SUCCESS(rv, rv);
2812  }
2813 
2814  return NS_OK;
2815 }
2816 
2817 NS_IMETHODIMP
2818 sbLocalDatabaseTreeView::OnCurrentIndexChanged()
2819 {
2820  nsresult rv;
2821  PRInt32 currentIndex;
2822 
2823  if (mRealSelection && mViewSelection) {
2824  rv = mViewSelection->GetCurrentIndex(&currentIndex);
2825  NS_ENSURE_SUCCESS(rv, rv);
2826 
2827  PRBool treeIsSelected;
2828  rv = mRealSelection->IsSelected(currentIndex, &treeIsSelected);
2829  NS_ENSURE_SUCCESS(rv, rv);
2830 
2831  // If this row is not selected, do a toggle, which
2832  // will fire a select event and set the current index
2833  if (!treeIsSelected) {
2834  rv = mRealSelection->ToggleSelect(currentIndex);
2835  NS_ENSURE_SUCCESS(rv, rv);
2836  } else {
2837  // Otherwise, already part of the selection, so just change current index
2838  rv = mRealSelection->SetCurrentIndex(currentIndex);
2839  NS_ENSURE_SUCCESS(rv, rv);
2840  }
2841  }
2842 
2843  return NS_OK;
2844 }
2845 
2846 // sbIPlayQueueServiceListener
2847 NS_IMETHODIMP
2848 sbLocalDatabaseTreeView::OnIndexUpdated(PRUint32 aToIndex)
2849 {
2850  nsresult rv;
2851 
2852 
2853  // xxx slloyd The play queue index is an index into the unfiltered
2854  // medialist. Applying style properties using this index only works if there
2855  // are no filters applied to the list. We have no plans for UI that allows
2856  // users to filter lists, but if an add-on developer filters a view of the
2857  // queue the visual styling will be off. See Bug 21878.
2858  PRUint32 oldIndex = mPlayQueueIndex;
2859  mPlayQueueIndex = aToIndex;
2860 
2861  // Invalidate rows between the old index and the new index.
2862  if (mTreeBoxObject) {
2863  if (oldIndex < mPlayQueueIndex) {
2864  rv = mTreeBoxObject->InvalidateRange(oldIndex, mPlayQueueIndex);
2865  NS_ENSURE_SUCCESS(rv, rv);
2866  } else {
2867  rv = mTreeBoxObject->InvalidateRange(mPlayQueueIndex, oldIndex);
2868  NS_ENSURE_SUCCESS(rv, rv);
2869  }
2870  }
2871  return NS_OK;
2872 }
2873 
2874 NS_IMETHODIMP
2875 sbLocalDatabaseTreeView::OnQueueOperationStarted()
2876 {
2877  return NS_OK;
2878 }
2879 
2880 NS_IMETHODIMP
2881 sbLocalDatabaseTreeView::OnQueueOperationCompleted()
2882 {
2883  return NS_OK;
2884 }
2885 
2886 // nsIObserver
2887 NS_IMETHODIMP
2888 sbLocalDatabaseTreeView::Observe(nsISupports* aSubject,
2889  const char* aTopic,
2890  const PRUnichar* aData)
2891 {
2892  NS_ENSURE_ARG_POINTER(aTopic);
2893  nsresult rv;
2894 
2895  if (mTreeBoxObject && !strcmp(SB_INVALIDATE_ALL_TREEVIEWS_TOPIC, aTopic)) {
2896  rv = mTreeBoxObject->Invalidate();
2897  NS_ENSURE_SUCCESS(rv, rv);
2898  }
2899 
2900  return NS_OK;
2901 }
2902 
2903 // nsIClassInfo
2904 NS_IMETHODIMP
2905 sbLocalDatabaseTreeView::GetInterfaces(PRUint32* count, nsIID*** array)
2906 {
2907  return NS_CI_INTERFACE_GETTER_NAME(sbLocalDatabaseTreeView)(count, array);
2908 }
2909 
2910 NS_IMETHODIMP
2911 sbLocalDatabaseTreeView::GetHelperForLanguage(PRUint32 language,
2912  nsISupports** _retval)
2913 {
2914  *_retval = nsnull;
2915  return NS_OK;
2916 }
2917 
2918 NS_IMETHODIMP
2919 sbLocalDatabaseTreeView::GetContractID(char** aContractID)
2920 {
2921  *aContractID = nsnull;
2922  return NS_OK;
2923 }
2924 
2925 NS_IMETHODIMP
2926 sbLocalDatabaseTreeView::GetClassDescription(char** aClassDescription)
2927 {
2928  *aClassDescription = nsnull;
2929  return NS_OK;
2930 }
2931 
2932 NS_IMETHODIMP
2933 sbLocalDatabaseTreeView::GetClassID(nsCID** aClassID)
2934 {
2935  *aClassID = nsnull;
2936  return NS_OK;
2937 }
2938 
2939 NS_IMETHODIMP
2940 sbLocalDatabaseTreeView::GetImplementationLanguage(PRUint32* aImplementationLanguage)
2941 {
2942  *aImplementationLanguage = nsIProgrammingLanguage::CPLUSPLUS;
2943  return NS_OK;
2944 }
2945 
2946 NS_IMETHODIMP
2947 sbLocalDatabaseTreeView::GetFlags(PRUint32 *aFlags)
2948 {
2949  *aFlags = 0;
2950  return NS_OK;
2951 }
2952 
2953 NS_IMETHODIMP
2954 sbLocalDatabaseTreeView::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
2955 {
2956  return NS_ERROR_NOT_AVAILABLE;
2957 }
2958 
2960  nsISerializable);
2961 
2963  mSelectionIsAll(PR_FALSE)
2964 {
2965 }
2966 
2967 NS_IMETHODIMP
2968 sbLocalDatabaseTreeViewState::Read(nsIObjectInputStream* aStream)
2969 {
2970  NS_ENSURE_ARG_POINTER(aStream);
2971 
2972  nsresult rv;
2973 
2974  rv = aStream->ReadObject(PR_TRUE, getter_AddRefs(mSort));
2975  NS_ENSURE_SUCCESS(rv, rv);
2976 
2977  PRUint32 selectionCount;
2978  rv = aStream->Read32(&selectionCount);
2979  NS_ENSURE_SUCCESS(rv, rv);
2980 
2981  for (PRUint32 i = 0; i < selectionCount; i++) {
2982  nsString key;
2983  nsString entry;
2984 
2985  rv = aStream->ReadString(key);
2986  NS_ENSURE_SUCCESS(rv, rv);
2987 
2988  rv = aStream->ReadString(entry);
2989  NS_ENSURE_SUCCESS(rv, rv);
2990 
2991  PRBool success = mSelectionList.Put(key, entry);
2992  NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
2993  }
2994 
2995  PRBool selectionIsAll;
2996  rv = aStream->ReadBoolean(&selectionIsAll);
2997  NS_ENSURE_SUCCESS(rv, rv);
2998  mSelectionIsAll = selectionIsAll;
2999 
3000  return NS_OK;
3001 }
3002 
3003 NS_IMETHODIMP
3004 sbLocalDatabaseTreeViewState::Write(nsIObjectOutputStream* aStream)
3005 {
3006  NS_ENSURE_ARG_POINTER(aStream);
3007 
3008  nsresult rv;
3009 
3010  rv = aStream->WriteObject(mSort, PR_TRUE);
3011  NS_ENSURE_SUCCESS(rv, rv);
3012 
3013  rv = aStream->Write32(mSelectionList.Count());
3014  NS_ENSURE_SUCCESS(rv, rv);
3015 
3016  mSelectionList.EnumerateRead(SB_SerializeSelectionListCallback, aStream);
3017 
3018  rv = aStream->WriteBoolean(mSelectionIsAll);
3019  NS_ENSURE_SUCCESS(rv, rv);
3020 
3021  return NS_OK;
3022 }
3023 
3024 nsresult
3026 {
3027  PRBool success = mSelectionList.Init();
3028  NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
3029 
3030  return NS_OK;
3031 }
3032 
3033 nsresult
3035 {
3036  nsresult rv;
3037  nsString buff;
3038  nsString temp;
3039 
3040  rv = mSort->ToString(temp);
3041  NS_ENSURE_SUCCESS(rv, rv);
3042 
3043  buff.Assign(temp);
3044 
3045  buff.AppendLiteral(" selection ");
3046  if (mSelectionIsAll) {
3047  buff.AppendLiteral("is all");
3048  }
3049  else {
3050  buff.AppendInt(mSelectionList.Count());
3051  buff.AppendLiteral(" items");
3052  }
3053 
3054  aStr = buff;
3055 
3056  return NS_OK;
3057 }
nsDataHashtable< nsStringHashKey, nsString > sbSelectionList
classDescription entry
Definition: FeedWriter.js:1427
A tree view that views a media list view.
NS_IMPL_ISUPPORTS1(sbLocalDatabaseTreeViewState, nsISerializable)
return NS_OK
static nsCOMPtr< nsIObserverService > observerService
Definition: UnityProxy.cpp:6
#define SB_STRING_BUNDLE_CHROME_URL
const unsigned long STATUS_PLAYING
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
inArray array
PLDHashOperator PR_CALLBACK SB_SelectionListGuidsToTArrayCallback(nsStringHashKey::KeyType aKey, nsString aEntry, void *aUserData)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
const unsigned long TRACK_CHANGE
Track playing has changed.
nsresult TokenizeProperties(const nsAString &aProperties, nsISupportsArray *aAtomArray)
Parses a string and separates space-delimited substrings into nsIAtom elements.
#define SB_PROPERTY_ORDINAL
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
var converter
#define SONGBIRD_LIBRARYSORT_CONTRACTID
Definition: sbLibraryCID.h:64
#define TRACE(args)
#define SB_MUTABLEPROPERTYARRAY_CONTRACTID
void SetShouldPreventRebuild(PRBool aShouldPreventRebuild)
sbAutoSuppressArrayInvalidation(sbILocalDatabaseGUIDArray *aArray)
var language
Definition: Info.js:44
const unsigned long STREAM_STOP
Stream was stopped.
#define SB_PROPERTY_ISSORTABLE
A distinct view on a given media list.
#define SB_PROPERTYMANAGER_CONTRACTID
NS_IMPL_THREADSAFE_ISUPPORTS10(sbLocalDatabaseTreeView, nsIClassInfo, nsIObserver, nsISupportsWeakReference, nsITreeView, sbILocalDatabaseGUIDArrayListener, sbILocalDatabaseTreeView, sbIMediaListViewTreeView, sbIMediacoreEventListener, sbIMediaListViewSelectionListener, sbIPlayQueueServiceListener) NS_IMPL_CI_INTERFACE_GETTER9(sbLocalDatabaseTreeView
Definition of the sbIMediacoreEvent interface.
nsCOMPtr< sbILibrarySort > mSort
const unsigned long STREAM_START
Stream has started.
var count
Definition: test_bug7406.js:32
#define SB_PROPERTY_CREATED
const unsigned long STATUS_BUFFERING
nsresult Init(sbLocalDatabaseMediaListView *aListView, sbILocalDatabaseGUIDArray *aArray, sbIPropertyArray *aCurrentSort, sbLocalDatabaseTreeViewState *aState)
Listener for the Play Queue service.
PRBool intersection(PRInt32 start1, PRInt32 end1, PRInt32 start2, PRInt32 end2, PRInt32 &intersectStart, PRInt32 &intersectEnd)
const unsigned long TRACK_INDEX_CHANGE
Index in view of item currently playing has changed.
nsresult OnTrackIndexChange(sbIMediacoreEvent *aEvent)
#define SB_PROPERTY_ISREADONLY
An interface used to describe a metadata property for use by the UI and other sbILibrary interfaces (...
nsISerializable
var columns
#define SB_PROPERTY_ORIGIN_IS_IN_MAIN_LIBRARY
Observer to implement the nsITreeView methods that a sbIMediaListViewTreeView does not...
sbAutoUpdateBatch(nsITreeBoxObject *aTreeBoxObject)
#define BAD_CSS_CHARS
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
PLDHashOperator PR_CALLBACK SB_CopySelectionListCallback(nsStringHashKey::KeyType aKey, nsString aEntry, void *aUserData)
inst dpDiv empty().append(this._generateHTML(inst)).find('iframe.ui-datepicker-cover').css(
NS_IMETHOD GetSelection(sbLocalDatabaseMediaListViewSelectionState **aState)
Listener interface that gets notified on selection changes.
countRef value
Definition: FeedWriter.js:1423
static void AppendInt(nsAString &str, PRInt64 val)
[USER CODE SHOULD NOT REFERENCE THIS CLASS]
attribute sbIMediaListViewTreeViewObserver observer
Get and set an observer for this media list tree view. This observer must support nsIWeakReference...
#define LOG(args)
const unsigned long STATUS_PAUSED
Control the sort settings on a media list.
bind("getData.datepicker", function(event, key){return this._get(inst, key);})
#define min(a, b)
restoreWindow aState
nsresult GetState(sbLocalDatabaseTreeViewState **aState)
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
const unsigned long STREAM_END
End of stream.
_getSelectedPageStyle s i
return first
NS_DECL_ISUPPORTS NS_DECL_NSISERIALIZABLE nsresult Init()
_updateTextAndScrollDataForFrame aData
#define SB_INVALIDATE_ALL_TREEVIEWS_TOPIC
PLDHashOperator PR_CALLBACK SB_SerializeSelectionListCallback(nsStringHashKey::KeyType aKey, nsString aEntry, void *aUserData)