Skip to content
This repository

grouping by movie set outside of the database #1418

Merged
5 commits merged into from over 1 year ago

3 participants

Sascha Montellese jmarshallnz Voyager1
Sascha Montellese
Collaborator

This is more of an RFC than an actual PR as I'd like to get some opinions on the general idea and the implementation.

A while ago I had a discussion with @jmarshallnz about doing the grouping of movies into movie sets outside of the videodatabase. The advantage would be that the code GetMoviesByWhere would become a lot easier and GetMoviesNav/GetMoviesByWhere will return a list of movies and not a mixed list of movies and movie sets. The grouping into movie sets would then happen before the movies are displayed in the GUI. I had this idea when I was working on the advanced filtering. When applying a filter it can happen that a filter only matches one of the movies in a movie set so XBMC should only show that single movie and not the whole movie set. While that's not a problem to achieve the problem arises when it comes to loading the artwork for items etc which is done in the background and on the initial list. So the "new" item which was previously part of a movie set does not have any artwork and other details assigned yet.

So I have written a utility class called GroupUtils which is called from CGUIWindowVideoBase after a list of movies has been retrieved from the database and filtered. It currently only supports grouping by movie sets (based on CVideoInfoTag::m_setId).

So what are your opinions? Does the grouping outside of the database make sense at all?

jmarshallnz
Sascha Montellese
Collaborator

The current implementation uses the resolution and the language of the first audio stream to display the different versions of a movie so e.g. "1080p (English)" and "720p (English)" etc but obviously that won't work in all cases.

@jmarshallnz: Any opinion on the general idea of grouping outside of the db? I know we discussed this once before but maybe you've changed your opinion after seeing the implementation. I can remove the movie grouping because obviously that needs more thought and a better approach.

jmarshallnz
Sascha Montellese
Collaborator

I've removed all the commits related to grouping by the same movie so that only the code for grouping by movie sets outside of the database remains. Should make it easier to review.

xbmc/video/VideoDatabase.cpp
((24 lines not shown))
5608  
-      GetSetsByWhere(setUrl.ToString(), setsFilter, setItems);
5609  
-
5610  
-      CStdString movieSetsWhere;
5611  
-      if (setItems.Size() > 0)
5612  
-      {
5613  
-        movieSetsWhere = "movieview.idMovie NOT IN (SELECT idMovie FROM movieview WHERE movieview.idSet IN (";
5614  
-        for (int index = 0; index < setItems.Size(); index++)
5615  
-          movieSetsWhere.AppendFormat("%s%d", index > 0 ? "," : "", setItems[index]->GetVideoInfoTag()->m_iDbId);
5616  
-        movieSetsWhere += "))";
5617  
-
5618  
-        extFilter.AppendWhere(movieSetsWhere);
5619  
-      }
5620  
-    }
5621  
-
5622  
-    if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
  5501
+        if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
1
jmarshallnz Owner

A few whitespace issues (there's some extra spaces in front of sorting.sortAttributes above as well)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
xbmc/windows/GUIMediaWindow.cpp
... ...
@@ -863,6 +864,10 @@ void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
863 864
   CStdString filter(GetProperty("filter").asString());
864 865
 
865 866
   GetFilteredItems(filter, items);
  867
+
  868
+  // Should these items be saved to the hdd
  869
+  if (items.CacheToDiscAlways())
  870
+    items.Save(GetID());
2
jmarshallnz Owner

Hmm, this saves the filtered list, not the unfiltered, or is GetFilteredItems() here supposed to be removed.

Sascha Montellese Collaborator

Hm I'll have to look into it. I'm getting confused in CGUIMediaWindow as I have multiple branches with changes in this area (advanced library filtering e.g.). It should filter the unfiltered list but maybe I messed this up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
jmarshallnz
Owner

Only thing I don't like is the separate queries per set. You could potentially get rid of that by joining the set table (there's only a single set per movie) though, so those can go away I think.

Nice work!

Sascha Montellese
Collaborator

Yeah this code is from before we went the "one set per movie" road so once rebased and updated the extra queries to get the set's names aren't necessary anymore because in mainline that information is already stored in the movieview.

Sascha Montellese
Collaborator

Based on the comments and looking at the current state of the code I think I messed up the commits when I pulled the "group by movie" functionality out because the original code didn't have any CGUIMediaWindow::FormatAndSort() anymore but had seperate calls Format() and Sort() and OnFinalizeItem() wasn't responsible for the filtering anymore but FilterAndGroup() was. Will have to check the original code and fix this up. Sorry for that.

Sascha Montellese
Collaborator

Yup see Montellese@master...grouping_by_movie#diff-24 for the changes to CGUIMediaWindow in the initial form. I must have accidentally removed those changes when I removed part of the initial changes. The order should be

GetDirectory() -> ... -> FormatItems() -> OnFinalizeItems(cache unfiltered list and don't do any filtering) -> FilterAndGroup(first do the filtering then the grouping) -> SortItems()

Voyager1
Collaborator

agree with both the accidentally removed changes and with the order. If you need someone to do a bit of testing before you merge, count me in!

Sascha Montellese
Collaborator

OK I have fixed CGUIMediaWindow up as it was in the original PR, fixed the cosmetics and removed the extra db lookups for the set name as it's already available in the movie's CVideoInfoTag::m_strSet. So the code is now ready for a proper review (sorry again for the mess).

During my tests I noticed one thing that still needs fixing but I'm at a loss right now. When I enter the list of movies the movies are properly grouped into the existing sets but in list view sorted by name the "rating" value isn't displayed for the set. If I switch to e.g. sorting by year, the "year" value shows fine and when I go back to sorted by name the "rating" value is also displayed fine. This is independent of the sort method and property, it's just that when entering the list the first time the property is not displayed for sets. I've added some debug logging after the call to FilterAndGroup() and SortItems() in CGUIMediaWindow::Update() and the rating is stored in the set's CVideoInfoTag. @jmarshallnz any clue why this could be happening?

Voyager1
Collaborator

Would it be related to the place where you call the Format() method on the group items? I had a similar (not sure) issue here: #1288

Sascha Montellese
Collaborator

You're right, that's most likely it. FormatItems() is called before FilterAndGroup() (question is if it could be moved to be called after calling FilterAndGroup() which would actually allow to re-unite the calls to FormatItems() and SortItems() into FormatAndSort()) so it formats the original list of items and isn't applied to the CFileItemPtr containing the set. @jmarshallnz any particular reason why FormatItems() would have to be called before OnFinalizeItems (which essentially copies the item list to m_unfilteredItems and caches it to disc) and before FilterAndGroup()?

Voyager1
Collaborator

I don't know whether there is any risk with Filtering needing the result (values) of formatting, or worse, you could end up not formatting the items that are actually inside groups. So what if you just call FormatItems once more as part of GroupItems(), only for those "group" items? Sounds simpler than it is, you'd probably need to do that in the GroupUtils...

Sascha Montellese
Collaborator

Well filtering happens through the database (except for the simple title filter (which will soon be replaced) which operates on the label but that filter is always reset before calling CGUIMediaWindow::Update()) so I don't really see any issues there. Not formatting the items which are grouped together is not a problem either because they are not displayed so no need for them to be formatted in that view.
Sure I could just call FormatItems again in CGUIWindowVideoBase::GetGroupedItems() when GroupUtils told me that the list has changed but getting the view from the database and doing the formatting is a rather "expensive" process (especially on embedded devices) so if possible I'd like to avoid that. And doing it in GroupUtils is out of the question as a utils method shouldn't really need to call back into CGUIMediaWindow IMO.

jmarshallnz
Owner

One presumes it's due to the title filtering applying to the label - can't filter on the label if it's not formatted up yet. If the filtering never looks at the label, then this order isn't needed.

Sascha Montellese
Collaborator

The title filter is always reset in CGUIMediaWindow::GetDirectory() which is called at the beginning of CGUIMediaWindow::Update() so at the time we do the filtering in Update() there's actually nothing to filter because it was just reset a bit earlier. I'll see if moving it past the FilterAndGroup() will cause any obvious issues.

Sascha Montellese
Collaborator

I have cleaned up the necessary changes to CGUIMediaWindow and it's a lot cleaner now. But there's still one issue with the thumb of the grouped item not showing up because the thumbloader operates on m_unfilteredItems. Changing this to m_vecItems works but only for unfiltered lists. Once a filter is applied the grouped item is a new CFileItemPtr instance and therefore can't use the details of the old one from the unfiltered list.

Sascha Montellese
Collaborator

OK I found the right spot to call the videothumbloader (see f7e5761) in CGUIWindowVideoBase::GetGroupedItems() which is called after every filtering i.e. both after the filtering done during CGUIMediaWindow::Update(), after using the title filter in a library view and after changing the "Show/Hide watched" toggle. The call to the thumbloader in CGUIWindowVideoBase::Update() could probably be removed but I left it there just in case.

Voyager1
Collaborator

@Montellese - did some testing with this branch and it looks good. I get the impression that when toggling hide watched status, some thumbs are reloaded each time even though the item is unwatched (and stays on the view). Any idea why?

Sascha Montellese
Collaborator

All the thumbs for the sets are reloaded because everytime you change the list, the grouping is re-done which results in a new CFileItemPtr for any set. Any other thumbs shouldn't be reloading or rather I can't think of a reason why they should.

Voyager1
Collaborator

update2 - I think I'm becoming crazy ^_^ - the few thumbs that briefly flashed were ALL sets (I didn't know it for some of them). So this is all good!

Sascha Montellese
Collaborator

I updated this PR in case we want to go this route instead of #1631 to solve the problem of additional items appearing in the list after filtering.

Unfortunately there are (at least) 3 issues I'm aware of:

  • There's no way to turn of grouping by sets for smartplaylists which would e.g. be desirable for the recently added movies list. This is already a problem in the current code and is not caused by these changes. This would probably need some additional configurable value in CSmartPlaylist to solve.
  • Sorted smartplaylists are messed up after doing the grouping stuff because the playlist order is based on the original items and the sets created during the grouping don't fit into that pre-sorted list anymore. I can't see any easy fix for this caused by the current intermediary state our sorting code is in.
  • Because lists now change on every run of Update() (as long as there are sets and grouping by sets is enabled), the list of movies jumps on every list refresh (e.g. when marking a movie as watched). The selected item stays the same but it usually jumps to the top or bottom of the currently visible area even if it was somewhere in between before the refresh. I'm not 100% how this is handled.
jmarshallnz
Owner

Is 3 already present anyway, perhaps only if the list is filtered? I'm not sure why the old technique wouldn't jump - filtering still would have taken place so that items could be removed (e.g. if hide watched was enabled). Is it worse now because the list always changes? The code that handles the selected item is in the list containers themselves, so something must be resetting the offset there (or it's doing something a bit silly).

Number 2 is tricky, agreed - you can't maintain the sorted order just by inserting items where the first movie was (sort by title will break) so the only way is to re-sort the list using the correct sort order, which you don't know atm from the playlist. The latter is related to the fact that "sort by playlist" is a duplicated sort mode in many library nodes, in addition to being non-descriptive.

Sascha Montellese
Collaborator

Concerning number 3 I have to admit that I'm not 100% sure what is happening. In current master when I'm in a list (filtered or not) and I mark an item as watched, all that happens is that the watched overlay appears and the selection moves on to the next item. But with this code the whole visible part of the list moves and the selection moves to the next item which is then the topmost visible item (in a vertical list) and the item that I just marked as watched isn't part of the visible list anymore.

Number 2 is kind of a deal-breaker here because it results in really odd results :-/ The list is already resorted after grouping but as you said, the grouped items don't match into the sort by playlist mode.

Sascha Montellese
Collaborator

See #1692 for a possible solution for number 1 and #1686 for a possible solution for number 2.

Sascha Montellese
Collaborator

@Voyager1 could I get you to play around with https://github.com/Montellese/xbmc/tree/grouping_outside_db? It contains all the commits necessary to get grouping outside of the db to work properly and should also fix any issues with limited lists (without any hacks like in #1631).

Voyager1
Collaborator

I have played with it before (#1418) and now again, still looking very good. I tried a those few filtering issues that broke before and it works fine now.

Does it make #1631 completely obsolete or just the hack I added?

Sascha Montellese
Collaborator

It makes #1631 completely obsolete as we alwaye retrieve a pure movie list so no need for any hacks and grouping is alwas done after filtering.

Voyager1
Collaborator

I hope it can still make the Frodo release :-)

Sascha Montellese
Collaborator

@jmarshallnz and @cptspiff: rebased and updated (had to add logic to not group movies into sets when in Recently Added Movies node).

xbmc/windows/GUIMediaWindow.cpp
... ...
@@ -968,17 +974,22 @@ void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
968 974
 
969 975
 }
970 976
 
  977
+// \brief This function will be called by Update() before
  978
+// any additional formating, filtering or sorting is applied.
1
jmarshallnz Owner

typo formatting

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
added some commits June 28, 2012
Sascha Montellese add GroupUtils for generic grouping of items in a list (e.g. into mov…
…ie sets)
3ff48bf
Sascha Montellese videodb: change retrieval of sets based on GroupUtils
GetSetsByWhere calls GetMoviesByWhere with custom JOIN clauses to only
retrieve movies being part of a set and the uses GroupUtils to group them
into sets. GetMoviesByWhere (and therefore GetMoviesNav) returns a list of
movies with no sets in it. To get sets GroupUtils::Group() has to be called
afterwards.
a88a8e7
Sascha Montellese [win32] updated project files 142ddea
Sascha Montellese media library: integrate grouping by sets fc44e6f
Sascha Montellese CGUIWindowVideoBase: always exectue the videothumbloader after filter…
…ing and grouping
8c517ac
Sascha Montellese
Collaborator

OK I messed up the commit/push yesterday but reflog saved my ass. I fixed the type @jmarshallnz mentioned and rebased onto latest master.

jmarshallnz
Owner

Signed off. Over to you @cptspiff

Deleted user ghost merged commit fc0d58b into from November 12, 2012
Deleted user ghost closed this November 12, 2012
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 5 unique commits by 1 author.

Nov 11, 2012
Sascha Montellese add GroupUtils for generic grouping of items in a list (e.g. into mov…
…ie sets)
3ff48bf
Sascha Montellese videodb: change retrieval of sets based on GroupUtils
GetSetsByWhere calls GetMoviesByWhere with custom JOIN clauses to only
retrieve movies being part of a set and the uses GroupUtils to group them
into sets. GetMoviesByWhere (and therefore GetMoviesNav) returns a list of
movies with no sets in it. To get sets GroupUtils::Group() has to be called
afterwards.
a88a8e7
Sascha Montellese [win32] updated project files 142ddea
Sascha Montellese media library: integrate grouping by sets fc44e6f
Sascha Montellese CGUIWindowVideoBase: always exectue the videothumbloader after filter…
…ing and grouping
8c517ac
This page is out of date. Refresh to see the latest.
2  project/VS2010Express/XBMC.vcxproj
@@ -1249,6 +1249,7 @@
1249 1249
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Testsuite|Win32'">true</ExcludedFromBuild>
1250 1250
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">true</ExcludedFromBuild>
1251 1251
     </ClCompile>
  1252
+    <ClCompile Include="..\..\xbmc\utils\GroupUtils.cpp" />
1252 1253
     <ClCompile Include="..\..\xbmc\utils\HTMLTable.cpp" />
1253 1254
     <ClCompile Include="..\..\xbmc\utils\HTMLUtil.cpp" />
1254 1255
     <ClCompile Include="..\..\xbmc\utils\HttpHeader.cpp" />
@@ -2395,6 +2396,7 @@
2395 2396
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug Testsuite|Win32'">true</ExcludedFromBuild>
2396 2397
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release (DirectX)|Win32'">true</ExcludedFromBuild>
2397 2398
     </ClInclude>
  2399
+    <ClInclude Include="..\..\xbmc\utils\GroupUtils.h" />
2398 2400
     <ClInclude Include="..\..\xbmc\utils\HTMLTable.h" />
2399 2401
     <ClInclude Include="..\..\xbmc\utils\HTMLUtil.h" />
2400 2402
     <ClInclude Include="..\..\xbmc\utils\HttpHeader.h" />
6  project/VS2010Express/XBMC.vcxproj.filters
@@ -2942,6 +2942,9 @@
2942 2942
     <ClCompile Include="..\..\xbmc\filesystem\HTTPFile.cpp">
2943 2943
       <Filter>filesystem</Filter>
2944 2944
     </ClCompile>
  2945
+    <ClCompile Include="..\..\xbmc\utils\GroupUtils.cpp">
  2946
+      <Filter>utils</Filter>
  2947
+    </ClCompile>
2945 2948
   </ItemGroup>
2946 2949
   <ItemGroup>
2947 2950
     <ClInclude Include="..\..\xbmc\win32\pch.h">
@@ -5749,6 +5752,9 @@
5749 5752
     <ClInclude Include="..\..\xbmc\filesystem\HTTPFile.h">
5750 5753
       <Filter>filesystem</Filter>
5751 5754
     </ClInclude>
  5755
+    <ClInclude Include="..\..\xbmc\utils\GroupUtils.h">
  5756
+      <Filter>utils</Filter>
  5757
+    </ClInclude>
5752 5758
   </ItemGroup>
5753 5759
   <ItemGroup>
5754 5760
     <ResourceCompile Include="..\..\xbmc\win32\XBMC_PC.rc">
2  xbmc/filesystem/SmartPlaylistDirectory.cpp
@@ -114,7 +114,7 @@ namespace XFILE
114 114
         videoUrl.AddOption(option, xsp);
115 115
         
116 116
         CDatabase::Filter dbfilter;
117  
-        success = db.GetSortedVideos(mediaType, videoUrl.ToString(), sorting, items, dbfilter, true);
  117
+        success = db.GetSortedVideos(mediaType, videoUrl.ToString(), sorting, items, dbfilter);
118 118
         db.Close();
119 119
 
120 120
         // if we retrieve a list of episodes and we didn't receive
2  xbmc/utils/EdenVideoArtUpdater.cpp
@@ -76,7 +76,7 @@ void CEdenVideoArtUpdater::Process()
76 76
   handle->SetTitle(g_localizeStrings.Get(12349));
77 77
 
78 78
   // movies
79  
-  db.GetMoviesByWhere("videodb://1/2/", CDatabase::Filter(), items, false);
  79
+  db.GetMoviesByWhere("videodb://1/2/", CDatabase::Filter(), items);
80 80
   for (int i = 0; i < items.Size(); i++)
81 81
   {
82 82
     CFileItemPtr item = items[i];
131  xbmc/utils/GroupUtils.cpp
... ...
@@ -0,0 +1,131 @@
  1
+/*
  2
+ *      Copyright (C) 2012 Team XBMC
  3
+ *      http://www.xbmc.org
  4
+ *
  5
+ *  This Program is free software; you can redistribute it and/or modify
  6
+ *  it under the terms of the GNU General Public License as published by
  7
+ *  the Free Software Foundation; either version 2, or (at your option)
  8
+ *  any later version.
  9
+ *
  10
+ *  This Program is distributed in the hope that it will be useful,
  11
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13
+ *  GNU General Public License for more details.
  14
+ *
  15
+ *  You should have received a copy of the GNU General Public License
  16
+ *  along with XBMC; see the file COPYING.  If not, see
  17
+ *  <http://www.gnu.org/licenses/>.
  18
+ *
  19
+ */
  20
+
  21
+#include <map>
  22
+#include <set>
  23
+
  24
+#include "GroupUtils.h"
  25
+#include "FileItem.h"
  26
+#include "utils/StringUtils.h"
  27
+#include "utils/Variant.h"
  28
+#include "video/VideoInfoTag.h"
  29
+
  30
+using namespace std;
  31
+
  32
+typedef map<int, set<CFileItemPtr> > SetMap;
  33
+
  34
+bool GroupUtils::Group(GroupBy groupBy, const CFileItemList &items, CFileItemList &groupedItems, GroupAttribute groupAttributes /* = GroupAttributeNone */)
  35
+{
  36
+  if (items.Size() <= 0 || groupBy == GroupByNone)
  37
+    return false;
  38
+
  39
+  SetMap setMap;
  40
+  for (int index = 0; index < items.Size(); index++)
  41
+  {
  42
+    bool add = true;
  43
+    const CFileItemPtr item = items.Get(index);
  44
+
  45
+    // group by sets
  46
+    if ((groupBy & GroupBySet) &&
  47
+        item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_iSetId > 0)
  48
+    {
  49
+      add = false;
  50
+      setMap[item->GetVideoInfoTag()->m_iSetId].insert(item);
  51
+    }
  52
+
  53
+    if (add)
  54
+      groupedItems.Add(item);
  55
+  }
  56
+
  57
+  if ((groupBy & GroupBySet) && setMap.size() > 0)
  58
+  {
  59
+    for (SetMap::const_iterator set = setMap.begin(); set != setMap.end(); set++)
  60
+    {
  61
+      // only one item in the set, so just re-add it
  62
+      if (set->second.size() == 1 && (groupAttributes & GroupAttributeIgnoreSingleItems))
  63
+      {
  64
+        groupedItems.Add(*set->second.begin());
  65
+        continue;
  66
+      }
  67
+
  68
+      CFileItemPtr pItem(new CFileItem((*set->second.begin())->GetVideoInfoTag()->m_strSet));
  69
+      pItem->GetVideoInfoTag()->m_iDbId = set->first;
  70
+      pItem->GetVideoInfoTag()->m_type = "set";
  71
+      pItem->SetPath(StringUtils::Format("videodb://1/7/%ld/", set->first));
  72
+      pItem->m_bIsFolder = true;
  73
+
  74
+      CVideoInfoTag* setInfo = pItem->GetVideoInfoTag();
  75
+      setInfo->m_strPath = pItem->GetPath();
  76
+      setInfo->m_strTitle = pItem->GetLabel();
  77
+
  78
+      int ratings = 0;
  79
+      int iWatched = 0; // have all the movies been played at least once?
  80
+      for (std::set<CFileItemPtr>::const_iterator movie = set->second.begin(); movie != set->second.end(); movie++)
  81
+      {
  82
+        CVideoInfoTag* movieInfo = (*movie)->GetVideoInfoTag();
  83
+        // handle rating
  84
+        if (movieInfo->m_fRating > 0.0f)
  85
+        {
  86
+          ratings++;
  87
+          setInfo->m_fRating += movieInfo->m_fRating;
  88
+        }
  89
+        
  90
+        // handle year
  91
+        if (movieInfo->m_iYear > setInfo->m_iYear)
  92
+          setInfo->m_iYear = movieInfo->m_iYear;
  93
+        
  94
+        // handle lastplayed
  95
+        if (movieInfo->m_lastPlayed.IsValid() && movieInfo->m_lastPlayed > setInfo->m_lastPlayed)
  96
+          setInfo->m_lastPlayed = movieInfo->m_lastPlayed;
  97
+        
  98
+        // handle dateadded
  99
+        if (movieInfo->m_dateAdded.IsValid() && movieInfo->m_dateAdded > setInfo->m_dateAdded)
  100
+          setInfo->m_dateAdded = movieInfo->m_dateAdded;
  101
+        
  102
+        // handle playcount/watched
  103
+        setInfo->m_playCount += movieInfo->m_playCount;
  104
+        if (movieInfo->m_playCount > 0)
  105
+          iWatched++;
  106
+      }
  107
+
  108
+      if (ratings > 1)
  109
+        pItem->GetVideoInfoTag()->m_fRating /= ratings;
  110
+        
  111
+      setInfo->m_playCount = iWatched >= (int)set->second.size() ? (setInfo->m_playCount / set->second.size()) : 0;
  112
+      pItem->SetProperty("total", (int)set->second.size());
  113
+      pItem->SetProperty("watched", iWatched);
  114
+      pItem->SetProperty("unwatched", (int)set->second.size() - iWatched);
  115
+      pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, setInfo->m_playCount > 0);
  116
+
  117
+      groupedItems.Add(pItem);
  118
+    }
  119
+  }
  120
+
  121
+  return true;
  122
+}
  123
+
  124
+bool GroupUtils::GroupAndSort(GroupBy groupBy, const CFileItemList &items, const SortDescription &sortDescription, CFileItemList &groupedItems, GroupAttribute groupAttributes /* = GroupAttributeNone */)
  125
+{
  126
+  if (!Group(groupBy, items, groupedItems, groupAttributes))
  127
+    return false;
  128
+
  129
+  groupedItems.Sort(sortDescription);
  130
+  return true;
  131
+}
42  xbmc/utils/GroupUtils.h
... ...
@@ -0,0 +1,42 @@
  1
+#pragma once
  2
+/*
  3
+ *      Copyright (C) 2012 Team XBMC
  4
+ *      http://www.xbmc.org
  5
+ *
  6
+ *  This Program is free software; you can redistribute it and/or modify
  7
+ *  it under the terms of the GNU General Public License as published by
  8
+ *  the Free Software Foundation; either version 2, or (at your option)
  9
+ *  any later version.
  10
+ *
  11
+ *  This Program is distributed in the hope that it will be useful,
  12
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14
+ *  GNU General Public License for more details.
  15
+ *
  16
+ *  You should have received a copy of the GNU General Public License
  17
+ *  along with XBMC; see the file COPYING.  If not, see
  18
+ *  <http://www.gnu.org/licenses/>.
  19
+ *
  20
+ */
  21
+
  22
+#include "SortUtils.h"
  23
+
  24
+class CFileItemList;
  25
+
  26
+// can be used as a flag
  27
+typedef enum {
  28
+  GroupByNone = 0x0,
  29
+  GroupBySet  = 0x1
  30
+} GroupBy;
  31
+
  32
+typedef enum {
  33
+  GroupAttributeNone              = 0x0,
  34
+  GroupAttributeIgnoreSingleItems = 0x1
  35
+} GroupAttribute;
  36
+
  37
+class GroupUtils
  38
+{
  39
+public:
  40
+  static bool Group(GroupBy groupBy, const CFileItemList &items, CFileItemList &groupedItems, GroupAttribute groupAttributes = GroupAttributeNone);
  41
+  static bool GroupAndSort(GroupBy groupBy, const CFileItemList &items, const SortDescription &sortDescription, CFileItemList &groupedItems, GroupAttribute groupAttributes = GroupAttributeNone);
  42
+};
1  xbmc/utils/Makefile
@@ -23,6 +23,7 @@ SRCS=AlarmClock.cpp \
23 23
      fstrcmp.c \
24 24
      fft.cpp \
25 25
      GLUtils.cpp \
  26
+     GroupUtils.cpp \
26 27
      HTMLTable.cpp \
27 28
      HTMLUtil.cpp \
28 29
      HttpHeader.cpp \
184  xbmc/video/VideoDatabase.cpp
@@ -55,6 +55,7 @@
55 55
 #include "URL.h"
56 56
 #include "video/VideoDbUrl.h"
57 57
 #include "playlists/SmartPlayList.h"
  58
+#include "utils/GroupUtils.h"
58 59
 
59 60
 using namespace std;
60 61
 using namespace dbiplus;
@@ -4815,111 +4816,21 @@ bool CVideoDatabase::GetSetsByWhere(const CStdString& strBaseDir, const Filter &
4815 4816
     if (!videoUrl.FromString(strBaseDir))
4816 4817
       return false;
4817 4818
 
4818  
-    CStdString strSQL = "SELECT movieview.*, sets.strSet FROM movieview"
4819  
-                        " JOIN sets ON movieview.idSet = sets.idSet ";
4820  
-    if (!filter.join.empty())
4821  
-      strSQL += filter.join;
4822  
-    if (!filter.where.empty())
4823  
-      strSQL += " WHERE (" + filter.where + ")";
4824  
-    strSQL += " ORDER BY sets.idSet";
4825  
-    if (!filter.order.empty())
4826  
-      strSQL += "," + filter.order;
4827  
-    if (!filter.limit.empty())
4828  
-      strSQL += " LIMIT " + filter.limit;
  4819
+    Filter setFilter = filter;
  4820
+    setFilter.join += " JOIN sets ON movieview.idSet = sets.idSet";
  4821
+    if (!setFilter.order.empty())
  4822
+      setFilter.order += ",";
  4823
+    setFilter.order += "sets.idSet";
4829 4824
 
4830  
-    int iRowsFound = RunQuery(strSQL);
4831  
-    if (iRowsFound <= 0)
4832  
-      return iRowsFound == 0;
4833  
-
4834  
-    map<int, CSetInfo> mapSets;
4835  
-    map<int, CSetInfo>::iterator it;
4836  
-    while (!m_pDS->eof())
4837  
-    {
4838  
-      if (g_settings.GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && !g_passwordManager.bMasterUser &&
4839  
-          !g_passwordManager.IsDatabasePathUnlocked(CStdString(m_pDS->fv("movieview.strPath").get_asString()),g_settings.m_videoSources))
4840  
-        continue;
4841  
-
4842  
-      // get the setid and check if we already have this set
4843  
-      int idSet = m_pDS->fv("movieview.idSet").get_asInt();
4844  
-      if ((it = mapSets.find(idSet)) == mapSets.end())
4845  
-      {
4846  
-        // add the set to the list of sets
4847  
-        CSetInfo set;
4848  
-        set.name = m_pDS->fv("sets.strSet").get_asString();
4849  
-
4850  
-        pair<map<int, CSetInfo>::iterator, bool> insertIt = mapSets.insert(make_pair(idSet, set));
4851  
-        it = insertIt.first;
4852  
-      }
4853  
-
4854  
-      // add the movie's details to the set
4855  
-      it->second.movies.push_back(GetDetailsForMovie(m_pDS));
4856  
-
4857  
-      m_pDS->next();
4858  
-    }
4859  
-    m_pDS->close();
4860  
-    
4861  
-    for (it = mapSets.begin(); it != mapSets.end(); it++)
4862  
-    {
4863  
-      // we only handle sets with at least 2 movies
4864  
-      if (it->second.movies.size() <= 0 ||
4865  
-         (ignoreSingleMovieSets && it->second.movies.size() == 1))
4866  
-        continue;
4867  
-
4868  
-      CFileItemPtr pItem(new CFileItem(it->second.name));
4869  
-      pItem->GetVideoInfoTag()->m_iDbId = it->first;
4870  
-      pItem->GetVideoInfoTag()->m_type = "set";
4871  
-
4872  
-      CVideoDbUrl itemUrl = videoUrl;
4873  
-      CStdString strDir; strDir.Format("%ld/", it->first);
4874  
-      itemUrl.AppendPath(strDir);
4875  
-      pItem->SetPath(itemUrl.ToString());
4876  
-
4877  
-      pItem->m_bIsFolder = true;
4878  
-      pItem->GetVideoInfoTag()->m_strPath = pItem->GetPath();
4879  
-      pItem->GetVideoInfoTag()->m_strTitle = pItem->GetLabel();
  4825
+    if (!GetMoviesByWhere(strBaseDir, setFilter, items))
  4826
+      return false;
4880 4827
 
4881  
-      // calculate the remaining metadata from the movies
4882  
-      int ratings = 0;
4883  
-      int iWatched = 0; // have all the movies been played at least once?
4884  
-      for (VECMOVIES::const_iterator movie = it->second.movies.begin(); movie != it->second.movies.end(); movie++)
4885  
-      {
4886  
-        // handle rating
4887  
-        if (movie->m_fRating > 0.0f)
4888  
-        {
4889  
-          ratings++;
4890  
-          pItem->GetVideoInfoTag()->m_fRating += movie->m_fRating;
4891  
-        }
4892  
-        
4893  
-        // handle year
4894  
-        if (movie->m_iYear > pItem->GetVideoInfoTag()->m_iYear)
4895  
-          pItem->GetVideoInfoTag()->m_iYear = movie->m_iYear;
4896  
-        
4897  
-        // handle lastplayed
4898  
-        if (movie->m_lastPlayed.IsValid() && movie->m_lastPlayed > pItem->GetVideoInfoTag()->m_lastPlayed)
4899  
-          pItem->GetVideoInfoTag()->m_lastPlayed = movie->m_lastPlayed;
4900  
-        
4901  
-        // handle dateadded
4902  
-        if (movie->m_dateAdded.IsValid() && movie->m_dateAdded > pItem->GetVideoInfoTag()->m_dateAdded)
4903  
-          pItem->GetVideoInfoTag()->m_dateAdded = movie->m_dateAdded;
4904  
-        
4905  
-        // handle playcount/watched
4906  
-        pItem->GetVideoInfoTag()->m_playCount += movie->m_playCount;
4907  
-        if (movie->m_playCount > 0)
4908  
-          iWatched++;
4909  
-      }
4910  
-      
4911  
-      if (ratings > 1)
4912  
-        pItem->GetVideoInfoTag()->m_fRating /= ratings;
4913  
-        
4914  
-      pItem->GetVideoInfoTag()->m_playCount = iWatched >= (int)it->second.movies.size() ? (pItem->GetVideoInfoTag()->m_playCount / it->second.movies.size()) : 0;
4915  
-      pItem->SetProperty("total", (int)it->second.movies.size());
4916  
-      pItem->SetProperty("watched", iWatched);
4917  
-      pItem->SetProperty("unwatched", (int)it->second.movies.size() - iWatched);
4918  
-      pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pItem->GetVideoInfoTag()->m_playCount > 0);
  4828
+    CFileItemList sets;
  4829
+    if (!GroupUtils::Group(GroupBySet, items, sets))
  4830
+      return false;
4919 4831
 
4920  
-      if (!items.Contains(pItem->GetPath()))
4921  
-        items.Add(pItem);
4922  
-    }
  4832
+    items.ClearItems();
  4833
+    items.Append(sets);
4923 4834
 
4924 4835
     return true;
4925 4836
   }
@@ -5686,7 +5597,7 @@ bool CVideoDatabase::GetSeasonsNav(const CStdString& strBaseDir, CFileItemList&
5686 5597
   return false;
5687 5598
 }
5688 5599
 
5689  
-bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */, bool fetchSets /* = false */)
  5600
+bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter /* = Filter() */)
5690 5601
 {
5691 5602
   if (NULL == m_pDB.get() || NULL == m_pDS.get())
5692 5603
     return false;
@@ -5710,7 +5621,7 @@ bool CVideoDatabase::GetSortedVideos(MediaType mediaType, const CStdString& strB
5710 5621
   switch (mediaType)
5711 5622
   {
5712 5623
   case MediaTypeMovie:
5713  
-    success = GetMoviesByWhere(strBaseDir, filter, items, fetchSets, sorting);
  5624
+    success = GetMoviesByWhere(strBaseDir, filter, items, sorting);
5714 5625
     break;
5715 5626
       
5716 5627
   case MediaTypeTvShow:
@@ -5760,10 +5671,10 @@ bool CVideoDatabase::GetMoviesNav(const CStdString& strBaseDir, CFileItemList& i
5760 5671
     videoUrl.AddOption("tagid", idTag);
5761 5672
 
5762 5673
   Filter filter;
5763  
-  return GetMoviesByWhere(videoUrl.ToString(), filter, items, idSet == -1, sortDescription);
  5674
+  return GetMoviesByWhere(videoUrl.ToString(), filter, items, sortDescription);
5764 5675
 }
5765 5676
 
5766  
-bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool fetchSets /* = false */, const SortDescription &sortDescription /* = SortDescription() */)
  5677
+bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription /* = SortDescription() */)
5767 5678
 {
5768 5679
   try
5769 5680
   {
@@ -5780,50 +5691,10 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
5780 5691
     if (!videoUrl.FromString(strBaseDir) || !GetFilter(videoUrl, extFilter, sorting))
5781 5692
       return false;
5782 5693
 
5783  
-    // if we have a "setid" option we don't want to retrieve sets
5784  
-    CVariant setId;
5785  
-    if (fetchSets && videoUrl.GetOption("setid", setId) &&
5786  
-        setId.isInteger() && setId.asInteger() > 0)
5787  
-      fetchSets = false;
5788  
-
5789 5694
     int total = -1;
5790 5695
 
5791 5696
     CStdString strSQL = "select %s from movieview ";
5792 5697
     CStdString strSQLExtra;
5793  
-    CFileItemList setItems;
5794  
-    if (fetchSets && g_guiSettings.GetBool("videolibrary.groupmoviesets"))
5795  
-    {
5796  
-      // user wants sets (and we're not fetching a particular set node), so grab all sets that match this where clause first
5797  
-      Filter setsFilter;
5798  
-      if (!extFilter.where.empty() || !extFilter.join.empty())
5799  
-      {
5800  
-        setsFilter.where = "movieview.idMovie in (select movieview.idMovie from movieview ";
5801  
-        if (!extFilter.join.empty())
5802  
-          setsFilter.where += extFilter.join;
5803  
-        if (!extFilter.where.empty())
5804  
-          setsFilter.where += " WHERE " + extFilter.where;
5805  
-        setsFilter.where += ")";
5806  
-      }
5807  
-
5808  
-      CVideoDbUrl setUrl;
5809  
-      if (!setUrl.FromString("videodb://1/7/"))
5810  
-        return false;
5811  
- 
5812  
-      setUrl.AddOptions(videoUrl.GetOptionsString());
5813  
-      GetSetsByWhere(setUrl.ToString(), setsFilter, setItems, true);
5814  
-
5815  
-      CStdString movieSetsWhere;
5816  
-      if (setItems.Size() > 0)
5817  
-      {
5818  
-        movieSetsWhere = "movieview.idMovie NOT IN (SELECT idMovie FROM movieview WHERE movieview.idSet IN (";
5819  
-        for (int index = 0; index < setItems.Size(); index++)
5820  
-          movieSetsWhere.AppendFormat("%s%d", index > 0 ? "," : "", setItems[index]->GetVideoInfoTag()->m_iDbId);
5821  
-        movieSetsWhere += "))";
5822  
-
5823  
-        extFilter.AppendWhere(movieSetsWhere);
5824  
-      }
5825  
-    }
5826  
-
5827 5698
     if (!CDatabase::BuildSQL(strSQLExtra, extFilter, strSQLExtra))
5828 5699
       return false;
5829 5700
 
@@ -5839,10 +5710,9 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
5839 5710
     strSQL = PrepareSQL(strSQL, !extFilter.fields.empty() ? extFilter.fields.c_str() : "*") + strSQLExtra;
5840 5711
 
5841 5712
     int iRowsFound = RunQuery(strSQL);
5842  
-    if (iRowsFound <= 0 && setItems.Size() == 0)
  5713
+    if (iRowsFound <= 0)
5843 5714
       return iRowsFound == 0;
5844 5715
 
5845  
-    iRowsFound += setItems.Size();
5846 5716
     // store the total value of items as a property
5847 5717
     if (total < iRowsFound)
5848 5718
       total = iRowsFound;
@@ -5851,16 +5721,7 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
5851 5721
     DatabaseResults results;
5852 5722
     results.reserve(iRowsFound);
5853 5723
 
5854  
-    // Add the previously retrieved sets
5855  
-    for (int index = 0; index < setItems.Size(); index++)
5856  
-    {
5857  
-      DatabaseResult result;
5858  
-      setItems[index]->ToSortable(result);
5859  
-      result[FieldRow] = (unsigned int)index;
5860  
-      results.push_back(result);
5861  
-    }
5862  
-
5863  
-    if (!SortUtils::SortFromDataset(sorting, MediaTypeMovie, m_pDS, results))
  5724
+    if (!SortUtils::SortFromDataset(sortDescription, MediaTypeMovie, m_pDS, results))
5864 5725
       return false;
5865 5726
 
5866 5727
     // get data from returned rows
@@ -5869,13 +5730,6 @@ bool CVideoDatabase::GetMoviesByWhere(const CStdString& strBaseDir, const Filter
5869 5730
     for (DatabaseResults::const_iterator it = results.begin(); it != results.end(); it++)
5870 5731
     {
5871 5732
       unsigned int targetRow = (unsigned int)it->at(FieldRow).asInteger();
5872  
-      if (targetRow < (unsigned int)setItems.Size())
5873  
-      {
5874  
-        items.Add(setItems[targetRow]);
5875  
-        continue;
5876  
-      }
5877  
-      targetRow -= setItems.Size();
5878  
-
5879 5733
       const dbiplus::sql_record* const record = data.at(targetRow);
5880 5734
 
5881 5735
       CVideoInfoTag movie = GetDetailsForMovie(record);
4  xbmc/video/VideoDatabase.h
@@ -640,14 +640,14 @@ class CVideoDatabase : public CDatabase
640 640
   bool ImportArtFromXML(const TiXmlNode *node, std::map<std::string, std::string> &artwork);
641 641
 
642 642
   // smart playlists and main retrieval work in these functions
643  
-  bool GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool fetchSets = false, const SortDescription &sortDescription = SortDescription());
  643
+  bool GetMoviesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription());
644 644
   bool GetSetsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool ignoreSingleMovieSets = false);
645 645
   bool GetTvShowsByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, const SortDescription &sortDescription = SortDescription());
646 646
   bool GetEpisodesByWhere(const CStdString& strBaseDir, const Filter &filter, CFileItemList& items, bool appendFullShowPath = true, const SortDescription &sortDescription = SortDescription());
647 647
   bool GetMusicVideosByWhere(const CStdString &baseDir, const Filter &filter, CFileItemList& items, bool checkLocks = true, const SortDescription &sortDescription = SortDescription());
648 648
   
649 649
   // retrieve sorted and limited items
650  
-  bool GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter = Filter(), bool fetchSets = false);
  650
+  bool GetSortedVideos(MediaType mediaType, const CStdString& strBaseDir, const SortDescription &sortDescription, CFileItemList& items, const Filter &filter = Filter());
651 651
 
652 652
   // partymode
653 653
   int GetMusicVideoCount(const CStdString& strWhere);
29  xbmc/video/windows/GUIWindowVideoBase.cpp
@@ -71,6 +71,7 @@
71 71
 #include "URL.h"
72 72
 #include "utils/EdenVideoArtUpdater.h"
73 73
 #include "GUIInfoManager.h"
  74
+#include "utils/GroupUtils.h"
74 75
 
75 76
 using namespace std;
76 77
 using namespace XFILE;
@@ -1787,7 +1788,9 @@ bool CGUIWindowVideoBase::Update(const CStdString &strDirectory, bool updateFilt
1787 1788
   if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath))
1788 1789
     return false;
1789 1790
 
1790  
-  m_thumbLoader.Load(*m_unfilteredItems);
  1791
+  // might already be running from GetGroupedItems
  1792
+  if (!m_thumbLoader.IsLoading())
  1793
+    m_thumbLoader.Load(*m_vecItems);
1791 1794
 
1792 1795
   return true;
1793 1796
 }
@@ -1839,8 +1842,30 @@ bool CGUIWindowVideoBase::StackingAvailable(const CFileItemList &items) const
1839 1842
            url.GetProtocol() == "playlistvideo");
1840 1843
 }
1841 1844
 
1842  
-void CGUIWindowVideoBase::OnPrepareFileItems(CFileItemList &items)
  1845
+void CGUIWindowVideoBase::GetGroupedItems(CFileItemList &items)
1843 1846
 {
  1847
+  CGUIMediaWindow::GetGroupedItems(items);
  1848
+
  1849
+  CQueryParams params;
  1850
+  CVideoDatabaseDirectory dir;
  1851
+  dir.GetQueryParams(items.GetPath(), params);
  1852
+  if (items.GetContent().Equals("movies") && params.GetSetId() <= 0 &&
  1853
+      CVideoDatabaseDirectory::GetDirectoryChildType(items.GetPath()) != NODE_TYPE_RECENTLY_ADDED_MOVIES &&
  1854
+      g_guiSettings.GetBool("videolibrary.groupmoviesets"))
  1855
+  {
  1856
+    CFileItemList groupedItems;
  1857
+    if (GroupUtils::Group(GroupBySet, items, groupedItems, GroupAttributeIgnoreSingleItems))
  1858
+    {
  1859
+      items.ClearItems();
  1860
+      items.Append(groupedItems);
  1861
+    }
  1862
+  }
  1863
+
  1864
+  // reload thumbs after filtering and grouping
  1865
+  if (m_thumbLoader.IsLoading())
  1866
+    m_thumbLoader.StopThread();
  1867
+
  1868
+  m_thumbLoader.Load(items);
1844 1869
 }
1845 1870
 
1846 1871
 bool CGUIWindowVideoBase::CheckFilterAdvanced(CFileItemList &items) const
2  xbmc/video/windows/GUIWindowVideoBase.h
@@ -88,7 +88,7 @@ class CGUIWindowVideoBase : public CGUIMediaWindow, public IBackgroundLoaderObse
88 88
   virtual bool Update(const CStdString &strDirectory, bool updateFilterPath = true);
89 89
   virtual bool GetDirectory(const CStdString &strDirectory, CFileItemList &items);
90 90
   virtual void OnItemLoaded(CFileItem* pItem) {};
91  
-  virtual void OnPrepareFileItems(CFileItemList &items);
  91
+  virtual void GetGroupedItems(CFileItemList &items);
92 92
 
93 93
   virtual bool CheckFilterAdvanced(CFileItemList &items) const;
94 94
   virtual bool CanContainFilter(const CStdString &strDirectory) const;
17  xbmc/video/windows/GUIWindowVideoNav.cpp
@@ -537,24 +537,9 @@ void CGUIWindowVideoNav::UpdateButtons()
537 537
 
538 538
 bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
539 539
 {
540  
-  bool listchanged = false;
541  
-  bool updateItems = false;
542  
-  if (!m_canFilterAdvanced)
543  
-    listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
544  
-  else
545  
-    listchanged = CGUIMediaWindow::GetAdvanceFilteredItems(items, updateItems);
546  
-
  540
+  bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
547 541
   listchanged |= ApplyWatchedFilter(items);
548 542
 
549  
-  // there are new items so we need to run the thumbloader
550  
-  if (updateItems)
551  
-  {
552  
-    if (m_thumbLoader.IsLoading())
553  
-      m_thumbLoader.StopThread();
554  
-
555  
-    m_thumbLoader.Load(items);
556  
-  }
557  
-
558 543
   return listchanged;
559 544
 }
560 545
 
102  xbmc/windows/GUIMediaWindow.cpp
@@ -617,10 +617,6 @@ void CGUIMediaWindow::SortItems(CFileItemList &items)
617 617
 
618 618
     if (!sorted)
619 619
       items.Sort(sortMethod, guiState->GetDisplaySortOrder());
620  
-
621  
-    // Should these items be saved to the hdd
622  
-    if (items.CacheToDiscAlways() && !IsFiltered())
623  
-      items.Save(GetID());
624 620
   }
625 621
 }
626 622
 
@@ -657,8 +653,9 @@ void CGUIMediaWindow::FormatAndSort(CFileItemList &items)
657 653
     LABEL_MASKS labelMasks;
658 654
     viewState->GetSortMethodLabelMasks(labelMasks);
659 655
     FormatItemLabels(items, labelMasks);
  656
+
  657
+    items.Sort(viewState->GetSortMethod(), viewState->GetDisplaySortOrder());
660 658
   }
661  
-  SortItems(items);
662 659
 }
663 660
 
664 661
 /*!
@@ -892,7 +889,16 @@ bool CGUIMediaWindow::Update(const CStdString &strDirectory, bool updateFilterPa
892 889
 
893 890
   m_guiState.reset(CGUIViewState::GetViewState(GetID(), *m_vecItems));
894 891
 
895  
-  FormatAndSort(*m_vecItems);
  892
+  // remember the original (untouched) list of items (for filtering etc)
  893
+  m_unfilteredItems->SetPath(m_vecItems->GetPath()); // use the original path - it'll likely be relied on for other things later.
  894
+  m_unfilteredItems->Append(*m_vecItems);
  895
+
  896
+  // Cache the list of items if possible
  897
+  OnCacheFileItems(*m_vecItems);
  898
+
  899
+  // Filter and group the items if necessary
  900
+  CStdString titleFilter = GetProperty("filter").asString();
  901
+  OnFilterItems(titleFilter);
896 902
 
897 903
   // Ask the devived class if it wants to do custom list operations,
898 904
   // eg. changing the label
@@ -968,17 +974,22 @@ void CGUIMediaWindow::OnPrepareFileItems(CFileItemList &items)
968 974
 
969 975
 }
970 976
 
  977
+// \brief This function will be called by Update() before
  978
+// any additional formatting, filtering or sorting is applied.
  979
+// Override this function to define a custom caching behaviour.
  980
+void CGUIMediaWindow::OnCacheFileItems(CFileItemList &items)
  981
+{
  982
+  // Should these items be saved to the hdd
  983
+  if (items.CacheToDiscAlways() && !IsFiltered())
  984
+    items.Save(GetID());
  985
+}
  986
+
971 987
 // \brief This function will be called by Update() after the
972 988
 // labels of the fileitems are formatted. Override this function
973 989
 // to modify the fileitems. Eg. to modify the item label
974 990
 void CGUIMediaWindow::OnFinalizeFileItems(CFileItemList &items)
975 991
 {
976  
-  m_unfilteredItems->SetPath(items.GetPath()); // use the original path - it'll likely be relied on for other things later.
977  
-  m_unfilteredItems->Append(items);
978  
-  
979  
-  CStdString filter(GetProperty("filter").asString());
980 992
 
981  
-  OnFilterItems(filter);
982 993
 }
983 994
 
984 995
 // \brief With this function you can react on a users click in the list/thumb panel.
@@ -1695,24 +1706,52 @@ void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1695 1706
   
1696 1707
   CFileItemList items(m_vecItems->GetPath()); // use the original path - it'll likely be relied on for other things later.
1697 1708
   items.Append(*m_unfilteredItems);
1698  
-  if (GetFilteredItems(filter, items) || m_vecItems->Size() != items.Size())
  1709
+  bool filtered = GetFilteredItems(filter, items);
  1710
+
  1711
+  m_vecItems->ClearItems();
  1712
+  // we need to clear the sort state and re-sort the items
  1713
+  m_vecItems->ClearSortState();
  1714
+  m_vecItems->Append(items);
  1715
+  
  1716
+  // if the filter has changed, get the new filter path
  1717
+  if (filtered && m_canFilterAdvanced)
1699 1718
   {
1700  
-    m_vecItems->ClearItems();
1701  
-    m_vecItems->Append(items);
  1719
+    if (items.HasProperty(PROPERTY_PATH_DB))
  1720
+      m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
  1721
+    else
  1722
+      m_strFilterPath = items.GetPath();
  1723
+  }
  1724
+  
  1725
+  GetGroupedItems(*m_vecItems);
  1726
+  FormatAndSort(*m_vecItems);
1702 1727
 
1703  
-    // we need to clear the sort state and re-sort the items
1704  
-    m_vecItems->ClearSortState();
1705  
-    SortItems(*m_vecItems);
  1728
+  // get the "filter" option
  1729
+  CStdString filterOption;
  1730
+  CURL filterUrl(m_strFilterPath);
  1731
+  if (filterUrl.HasOption("filter"))
  1732
+    filterOption = filterUrl.GetOption("filter");
  1733
+
  1734
+  // apply the "filter" option to any folder item so that
  1735
+  // the filter can be passed down to the sub-directory
  1736
+  for (int index = 0; index < m_vecItems->Size(); index++)
  1737
+  {
  1738
+    CFileItemPtr pItem = m_vecItems->Get(index);
  1739
+    // if the item is a folder we need to copy the path of
  1740
+    // the filtered item to be able to keep the applied filters
  1741
+    if (pItem->m_bIsFolder)
  1742
+    {
  1743
+      CURL itemUrl(pItem->GetPath());
  1744
+      itemUrl.SetOption("filter", filterOption);
  1745
+      pItem->SetPath(itemUrl.Get());
  1746
+    }
  1747
+  }
1706 1748
 
  1749
+  if (filtered)
  1750
+  {
1707 1751
     if (!m_canFilterAdvanced)
1708 1752
       SetProperty("filter", filter);
1709 1753
     else
1710 1754
     {
1711  
-      if (items.HasProperty(PROPERTY_PATH_DB))
1712  
-        m_strFilterPath = items.GetProperty(PROPERTY_PATH_DB).asString();
1713  
-      else
1714  
-        m_strFilterPath = items.GetPath();
1715  
-
1716 1755
       // to be able to select the same item as before we need to adjust
1717 1756
       // the path of the item i.e. add or remove the "filter=" URL option
1718 1757
       // but that's only necessary for folder items
@@ -1738,10 +1777,7 @@ void CGUIMediaWindow::OnFilterItems(const CStdString &filter)
1738 1777
 bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &items)
1739 1778
 {
1740 1779
   if (m_canFilterAdvanced)
1741  
-  {
1742  
-    bool hasNewItems;
1743  
-    return GetAdvanceFilteredItems(items, hasNewItems);
1744  
-  }
  1780
+    return GetAdvanceFilteredItems(items);
1745 1781
 
1746 1782
   CStdString trimmedFilter(filter);
1747 1783
   trimmedFilter.TrimLeft().ToLower();
@@ -1786,10 +1822,8 @@ bool CGUIMediaWindow::GetFilteredItems(const CStdString &filter, CFileItemList &
1786 1822
   return items.GetObjectCount() > 0;
1787 1823
 }
1788 1824
 
1789  
-bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items, bool &hasNewItems)
  1825
+bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items)
1790 1826
 {
1791  
-  hasNewItems = false;
1792  
-
1793 1827
   // don't run the advanced filter if the filter is empty
1794 1828
   // and there hasn't been a filter applied before which
1795 1829
   // would have to be removed
@@ -1831,11 +1865,6 @@ bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items, bool &hasNew
1831 1865
     map<CStdString, CFileItemPtr>::iterator itItem = lookup.find(path);
1832 1866
     if (itItem != lookup.end())
1833 1867
     {
1834  
-      // if the item is a folder we need to copy the path of
1835  
-      // the filtered item to be able to keep the applied filters
1836  
-      if (item->m_bIsFolder)
1837  
-        item->SetPath(itItem->second->GetPath());
1838  
-
1839 1868
       // add the item to the list of filtered items
1840 1869
       filteredItems.Add(item);
1841 1870
 
@@ -1846,10 +1875,7 @@ bool CGUIMediaWindow::GetAdvanceFilteredItems(CFileItemList &items, bool &hasNew
1846 1875
   }
1847 1876
 
1848 1877
   if (resultItems.Size() > 0)
1849  
-  {
1850  
-    filteredItems.Append(resultItems);
1851  
-    hasNewItems = true;
1852  
-  }
  1878
+    CLog::Log(LOGWARNING, "CGUIMediaWindow::GetAdvanceFilteredItems(): %d unknown items", resultItems.Size());
1853 1879
 
1854 1880
   items.ClearItems();
1855 1881
   items.Append(filteredItems);
4  xbmc/windows/GUIMediaWindow.h
@@ -86,7 +86,9 @@ class CGUIMediaWindow : public CGUIWindow
86 86
   virtual bool Refresh(bool clearCache = false);
87 87
   virtual void FormatAndSort(CFileItemList &items);
88 88
   virtual void OnPrepareFileItems(CFileItemList &items);
  89
+  virtual void OnCacheFileItems(CFileItemList &items);
89 90
   virtual void OnFinalizeFileItems(CFileItemList &items);
  91
+  virtual void GetGroupedItems(CFileItemList &items) { }
90 92
 
91 93
   void ClearFileItems();
92 94
   virtual void SortItems(CFileItemList &items);
@@ -123,7 +125,7 @@ class CGUIMediaWindow : public CGUIWindow
123 125
                      which were not present in the original list
124 126
   \sa GetFilteredItems
125 127
   */
126  
-  virtual bool GetAdvanceFilteredItems(CFileItemList &items, bool &hasNewItems);
  128
+  virtual bool GetAdvanceFilteredItems(CFileItemList &items);
127 129
 
128 130
   // check for a disc or connection
129 131
   virtual bool HaveDiscOrConnection(const CStdString& strPath, int iDriveType);
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.