Merge pull request #3819 from arnova/subtitles_for_stacks
[vuplus_xbmc] / xbmc / video / windows / GUIWindowVideoNav.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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 "GUIUserMessages.h"
22 #include "GUIWindowVideoNav.h"
23 #include "music/windows/GUIWindowMusicNav.h"
24 #include "utils/FileUtils.h"
25 #include "Util.h"
26 #include "utils/RegExp.h"
27 #include "PlayListPlayer.h"
28 #include "GUIPassword.h"
29 #include "dialogs/GUIDialogFileBrowser.h"
30 #include "filesystem/VideoDatabaseDirectory.h"
31 #include "playlists/PlayListFactory.h"
32 #include "dialogs/GUIDialogOK.h"
33 #include "addons/AddonManager.h"
34 #include "PartyModeManager.h"
35 #include "music/MusicDatabase.h"
36 #include "guilib/GUIWindowManager.h"
37 #include "dialogs/GUIDialogYesNo.h"
38 #include "dialogs/GUIDialogSelect.h"
39 #include "filesystem/Directory.h"
40 #include "filesystem/File.h"
41 #include "FileItem.h"
42 #include "Application.h"
43 #include "ApplicationMessenger.h"
44 #include "profiles/ProfilesManager.h"
45 #include "settings/AdvancedSettings.h"
46 #include "settings/MediaSettings.h"
47 #include "settings/MediaSourceSettings.h"
48 #include "settings/Settings.h"
49 #include "guilib/Key.h"
50 #include "guilib/LocalizeStrings.h"
51 #include "storage/MediaManager.h"
52 #include "utils/LegacyPathTranslation.h"
53 #include "utils/log.h"
54 #include "utils/URIUtils.h"
55 #include "utils/StringUtils.h"
56 #include "TextureCache.h"
57 #include "guilib/GUIKeyboardFactory.h"
58 #include "video/VideoInfoScanner.h"
59 #include "video/dialogs/GUIDialogVideoInfo.h"
60 #include "pvr/recordings/PVRRecording.h"
61
62 using namespace XFILE;
63 using namespace VIDEODATABASEDIRECTORY;
64 using namespace std;
65
66 #define CONTROL_BTNVIEWASICONS     2
67 #define CONTROL_BTNSORTBY          3
68 #define CONTROL_BTNSORTASC         4
69 #define CONTROL_BTNTYPE            5
70 #define CONTROL_BTNSEARCH          8
71 #define CONTROL_LABELFILES        12
72
73 #define CONTROL_BTN_FILTER        19
74 #define CONTROL_BTNSHOWMODE       10
75 #define CONTROL_BTNSHOWALL        14
76 #define CONTROL_UNLOCK            11
77
78 #define CONTROL_FILTER            15
79 #define CONTROL_BTNPARTYMODE      16
80 #define CONTROL_LABELEMPTY        18
81
82 #define CONTROL_UPDATE_LIBRARY    20
83
84 CGUIWindowVideoNav::CGUIWindowVideoNav(void)
85     : CGUIWindowVideoBase(WINDOW_VIDEO_NAV, "MyVideoNav.xml")
86 {
87   m_thumbLoader.SetObserver(this);
88 }
89
90 CGUIWindowVideoNav::~CGUIWindowVideoNav(void)
91 {
92 }
93
94 bool CGUIWindowVideoNav::OnAction(const CAction &action)
95 {
96   if (action.GetID() == ACTION_TOGGLE_WATCHED)
97   {
98     CFileItemPtr pItem = m_vecItems->Get(m_viewControl.GetSelectedItem());
99     if (pItem->IsParentFolder())
100       return false;
101     if (pItem && pItem->GetVideoInfoTag()->m_playCount == 0)
102       return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_MARK_WATCHED);
103     if (pItem && pItem->GetVideoInfoTag()->m_playCount > 0)
104       return OnContextButton(m_viewControl.GetSelectedItem(),CONTEXT_BUTTON_MARK_UNWATCHED);
105   }
106   return CGUIWindowVideoBase::OnAction(action);
107 }
108
109 bool CGUIWindowVideoNav::OnMessage(CGUIMessage& message)
110 {
111   switch (message.GetMessage())
112   {
113   case GUI_MSG_WINDOW_RESET:
114     m_vecItems->SetPath("");
115     break;
116   case GUI_MSG_WINDOW_DEINIT:
117     if (m_thumbLoader.IsLoading())
118       m_thumbLoader.StopThread();
119     break;
120   case GUI_MSG_WINDOW_INIT:
121     {
122       /* We don't want to show Autosourced items (ie removable pendrives, memorycards) in Library mode */
123       m_rootDir.AllowNonLocalSources(false);
124
125       SetProperty("flattened", CSettings::Get().GetBool("myvideos.flatten"));
126       if (message.GetNumStringParams() && message.GetStringParam(0).Equals("Files") &&
127           CMediaSourceSettings::Get().GetSources("video")->empty())
128       {
129         message.SetStringParam("");
130       }
131       
132       if (!CGUIWindowVideoBase::OnMessage(message))
133         return false;
134
135       return true;
136     }
137     break;
138
139   case GUI_MSG_CLICKED:
140     {
141       int iControl = message.GetSenderId();
142       if (iControl == CONTROL_BTNPARTYMODE)
143       {
144         if (g_partyModeManager.IsEnabled())
145           g_partyModeManager.Disable();
146         else
147         {
148           if (!g_partyModeManager.Enable(PARTYMODECONTEXT_VIDEO))
149           {
150             SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE,false);
151             return false;
152           }
153
154           // Playlist directory is the root of the playlist window
155           if (m_guiState.get()) m_guiState->SetPlaylistDirectory("playlistvideo://");
156
157           return true;
158         }
159         UpdateButtons();
160       }
161
162       if (iControl == CONTROL_BTNSEARCH)
163       {
164         OnSearch();
165       }
166       else if (iControl == CONTROL_BTNSHOWMODE)
167       {
168         CMediaSettings::Get().CycleWatchedMode(m_vecItems->GetContent());
169         CSettings::Get().Save();
170         OnFilterItems(GetProperty("filter").asString());
171         return true;
172       }
173       else if (iControl == CONTROL_BTNSHOWALL)
174       {
175         if (CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent()) == WatchedModeAll)
176           CMediaSettings::Get().SetWatchedMode(m_vecItems->GetContent(), WatchedModeUnwatched);
177         else
178           CMediaSettings::Get().SetWatchedMode(m_vecItems->GetContent(), WatchedModeAll);
179         CSettings::Get().Save();
180         OnFilterItems(GetProperty("filter").asString());
181         return true;
182       }
183       else if (iControl == CONTROL_UPDATE_LIBRARY)
184       {
185         if (!g_application.IsVideoScanning())
186           OnScan("");
187         else
188           g_application.StopVideoScan();
189         return true;
190       }
191     }
192     break;
193     // update the display
194     case GUI_MSG_SCAN_FINISHED:
195     case GUI_MSG_REFRESH_THUMBS:
196       Refresh();
197       break;
198   }
199   return CGUIWindowVideoBase::OnMessage(message);
200 }
201
202 CStdString CGUIWindowVideoNav::GetQuickpathName(const CStdString& strPath) const
203 {
204   CStdString path = CLegacyPathTranslation::TranslateVideoDbPath(strPath);
205   if (path.Equals("videodb://movies/genres/"))
206     return "MovieGenres";
207   else if (path.Equals("videodb://movies/titles/"))
208     return "MovieTitles";
209   else if (path.Equals("videodb://movies/years/"))
210     return "MovieYears";
211   else if (path.Equals("videodb://movies/actors/"))
212     return "MovieActors";
213   else if (path.Equals("videodb://movies/directors/"))
214     return "MovieDirectors";
215   else if (path.Equals("videodb://movies/studios/"))
216     return "MovieStudios";
217   else if (path.Equals("videodb://movies/sets/"))
218     return "MovieSets";
219   else if (path.Equals("videodb://movies/countries/"))
220     return "MovieCountries";
221   else if (path.Equals("videodb://movies/tags/"))
222     return "MovieTags";
223   else if (path.Equals("videodb://movies/"))
224     return "Movies";
225   else if (path.Equals("videodb://tvshows/genres/"))
226     return "TvShowGenres";
227   else if (path.Equals("videodb://tvshows/titles/"))
228     return "TvShowTitles";
229   else if (path.Equals("videodb://tvshows/years/"))
230     return "TvShowYears";
231   else if (path.Equals("videodb://tvshows/actors/"))
232     return "TvShowActors";
233   else if (path.Equals("videodb://tvshows/studios/"))
234     return "TvShowStudios";
235   else if (path.Equals("videodb://tvshows/tags/"))
236     return "TvShowTags";
237   else if (path.Equals("videodb://tvshows/"))
238     return "TvShows";
239   else if (path.Equals("videodb://musicvideos/genres/"))
240     return "MusicVideoGenres";
241   else if (path.Equals("videodb://musicvideos/titles/"))
242     return "MusicVideoTitles";
243   else if (path.Equals("videodb://musicvideos/years/"))
244     return "MusicVideoYears";
245   else if (path.Equals("videodb://musicvideos/artists/"))
246     return "MusicVideoArtists";
247   else if (path.Equals("videodb://musicvideos/albums/"))
248     return "MusicVideoDirectors";
249   else if (path.Equals("videodb://musicvideos/tags/"))
250     return "MusicVideoTags";
251   else if (path.Equals("videodb://musicvideos/"))
252     return "MusicVideos";
253   else if (path.Equals("videodb://recentlyaddedmovies/"))
254     return "RecentlyAddedMovies";
255   else if (path.Equals("videodb://recentlyaddedepisodes/"))
256     return "RecentlyAddedEpisodes";
257   else if (path.Equals("videodb://recentlyaddedmusicvideos/"))
258     return "RecentlyAddedMusicVideos";
259   else if (path.Equals("special://videoplaylists/"))
260     return "Playlists";
261   else if (path.Equals("sources://video/"))
262     return "Files";
263   else
264   {
265     CLog::Log(LOGERROR, "  CGUIWindowVideoNav::GetQuickpathName: Unknown parameter (%s)", strPath.c_str());
266     return strPath;
267   }
268 }
269
270 bool CGUIWindowVideoNav::GetDirectory(const CStdString &strDirectory, CFileItemList &items)
271 {
272   if (m_thumbLoader.IsLoading())
273     m_thumbLoader.StopThread();
274
275   items.ClearProperties();
276
277   bool bResult = CGUIWindowVideoBase::GetDirectory(strDirectory, items);
278   if (bResult)
279   {
280     if (items.IsVideoDb())
281     {
282       XFILE::CVideoDatabaseDirectory dir;
283       CQueryParams params;
284       dir.GetQueryParams(items.GetPath(),params);
285       VIDEODATABASEDIRECTORY::NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
286
287       // perform the flattening logic for tvshows with a single (unwatched) season (+ optional special season)
288       if (node == NODE_TYPE_SEASONS)
289       {
290         int itemsSize = items.GetObjectCount();
291         int firstIndex = items.Size() - itemsSize;
292         // check if the last item is the "All seasons" item which should be ignored for flattening
293         if (items[items.Size() - 1]->GetVideoInfoTag()->m_iSeason < 0)
294           itemsSize -= 1;
295
296         int iFlatten = CSettings::Get().GetInt("videolibrary.flattentvshows");
297         bool bFlatten = (itemsSize == 1 && iFlatten == 1) || iFlatten == 2 ||                              // flatten if one one season or if always flatten is enabled
298                         (itemsSize == 2 && iFlatten == 1 &&                                                // flatten if one season + specials
299                          (items[firstIndex]->GetVideoInfoTag()->m_iSeason == 0 || items[firstIndex + 1]->GetVideoInfoTag()->m_iSeason == 0));
300
301         if (iFlatten > 0 && !bFlatten && (WatchedMode)CMediaSettings::Get().GetWatchedMode("tvshows") == WatchedModeUnwatched)
302         {
303           int count = 0;
304           for(int i = 0; i < items.Size(); i++)
305           {
306             const CFileItemPtr item = items.Get(i);
307             if (item->GetProperty("unwatchedepisodes").asInteger() != 0 && item->GetVideoInfoTag()->m_iSeason > 0)
308               count++;
309           }
310           bFlatten = (count < 2); // flatten if there is only 1 unwatched season (not counting specials)
311         }
312
313         if (bFlatten)
314         { // flatten if one season or flatten always
315           items.Clear();
316
317           CVideoDbUrl videoUrl;
318           if (!videoUrl.FromString(items.GetPath()))
319             return false;
320
321           videoUrl.AppendPath("-2/");
322           return GetDirectory(videoUrl.ToString(), items);
323         }
324       }
325
326       items.SetArt("thumb", "");
327       if (node == VIDEODATABASEDIRECTORY::NODE_TYPE_EPISODES ||
328           node == NODE_TYPE_SEASONS                          ||
329           node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
330       {
331         CLog::Log(LOGDEBUG, "WindowVideoNav::GetDirectory");
332         // grab the show thumb
333         CVideoInfoTag details;
334         m_database.GetTvShowInfo("", details, params.GetTvShowId());
335         map<string, string> art;
336         if (m_database.GetArtForItem(details.m_iDbId, details.m_type, art))
337         {
338           items.AppendArt(art, "tvshow");
339           items.SetArtFallback("fanart", "tvshow.fanart");
340           if (node == NODE_TYPE_SEASONS)
341           { // set an art fallback for "thumb"
342             if (items.HasArt("tvshow.poster"))
343               items.SetArtFallback("thumb", "tvshow.poster");
344             else if (items.HasArt("tvshow.banner"))
345               items.SetArtFallback("thumb", "tvshow.banner");
346           }
347         }
348
349         // Grab fanart data
350         items.SetProperty("fanart_color1", details.m_fanart.GetColor(0));
351         items.SetProperty("fanart_color2", details.m_fanart.GetColor(1));
352         items.SetProperty("fanart_color3", details.m_fanart.GetColor(2));
353
354         // save the show description (showplot)
355         items.SetProperty("showplot", details.m_strPlot);
356
357         // the container folder thumb is the parent (i.e. season or show)
358         if (node == NODE_TYPE_EPISODES || node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
359         {
360           items.SetContent("episodes");
361           // grab the season thumb as the folder thumb
362           int seasonID = m_database.GetSeasonId(details.m_iDbId, params.GetSeason());
363           CGUIListItem::ArtMap seasonArt;
364           if (m_database.GetArtForItem(seasonID, "season", seasonArt))
365           {
366             items.AppendArt(art, "season");
367             // set an art fallback for "thumb"
368             if (items.HasArt("season.poster"))
369               items.SetArtFallback("thumb", "season.poster");
370             else if (items.HasArt("season.banner"))
371               items.SetArtFallback("thumb", "season.banner");
372           }
373         }
374         else
375           items.SetContent("seasons");
376       }
377       else if (node == NODE_TYPE_TITLE_MOVIES ||
378                node == NODE_TYPE_RECENTLY_ADDED_MOVIES)
379         items.SetContent("movies");
380       else if (node == NODE_TYPE_TITLE_TVSHOWS)
381         items.SetContent("tvshows");
382       else if (node == NODE_TYPE_TITLE_MUSICVIDEOS ||
383                node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
384         items.SetContent("musicvideos");
385       else if (node == NODE_TYPE_GENRE)
386         items.SetContent("genres");
387       else if (node == NODE_TYPE_COUNTRY)
388         items.SetContent("countries");
389       else if (node == NODE_TYPE_ACTOR)
390       {
391         if (params.GetContentType() == VIDEODB_CONTENT_MUSICVIDEOS)
392           items.SetContent("artists");
393         else
394           items.SetContent("actors");
395       }
396       else if (node == NODE_TYPE_DIRECTOR)
397         items.SetContent("directors");
398       else if (node == NODE_TYPE_STUDIO)
399         items.SetContent("studios");
400       else if (node == NODE_TYPE_YEAR)
401         items.SetContent("years");
402       else if (node == NODE_TYPE_MUSICVIDEOS_ALBUM)
403         items.SetContent("albums");
404       else if (node == NODE_TYPE_SETS)
405         items.SetContent("sets");
406       else if (node == NODE_TYPE_TAGS)
407         items.SetContent("tags");
408       else
409         items.SetContent("");
410     }
411     else if (!items.IsVirtualDirectoryRoot())
412     { // load info from the database
413       CStdString label;
414       if (items.GetLabel().empty() && m_rootDir.IsSource(items.GetPath(), CMediaSourceSettings::Get().GetSources("video"), &label)) 
415         items.SetLabel(label);
416       if (!items.IsSourcesPath())
417         LoadVideoInfo(items);
418     }
419
420     CVideoDbUrl videoUrl;
421     if (videoUrl.FromString(items.GetPath()) && items.GetContent() == "tags" &&
422        !items.Contains("newtag://" + videoUrl.GetType()))
423     {
424       CFileItemPtr newTag(new CFileItem("newtag://" + videoUrl.GetType(), false));
425       newTag->SetLabel(g_localizeStrings.Get(20462));
426       newTag->SetLabelPreformated(true);
427       newTag->SetSpecialSort(SortSpecialOnTop);
428       items.Add(newTag);
429     }
430   }
431   return bResult;
432 }
433
434 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items)
435 {
436   LoadVideoInfo(items, m_database);
437 }
438
439 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items, CVideoDatabase &database, bool allowReplaceLabels)
440 {
441   // TODO: this could possibly be threaded as per the music info loading,
442   //       we could also cache the info
443   if (!items.GetContent().empty() && !items.IsPlugin())
444     return; // don't load for listings that have content set and weren't created from plugins
445
446   CStdString content = items.GetContent();
447   // determine content only if it isn't set
448   if (content.empty())
449   {
450     content = database.GetContentForPath(items.GetPath());
451     items.SetContent(content.empty() ? "files" : content);
452   }
453
454   /*
455     If we have a matching item in the library, so we can assign the metadata to it. In addition, we can choose
456     * whether the item is stacked down (eg in the case of folders representing a single item)
457     * whether or not we assign the library's labels to the item, or leave the item as is.
458
459     As certain users (read: certain developers) don't want either of these to occur, we compromise by stacking
460     items down only if stacking is available and enabled.
461
462     Similarly, we assign the "clean" library labels to the item only if the "Replace filenames with library titles"
463     setting is enabled.
464     */
465   const bool stackItems    = items.GetProperty("isstacked").asBoolean() || (StackingAvailable(items) && CSettings::Get().GetBool("myvideos.stackvideos"));
466   const bool replaceLabels = allowReplaceLabels && CSettings::Get().GetBool("myvideos.replacelabels");
467
468   CFileItemList dbItems;
469   /* NOTE: In the future when GetItemsForPath returns all items regardless of whether they're "in the library"
470            we won't need the fetchedPlayCounts code, and can "simply" do this directly on absense of content. */
471   bool fetchedPlayCounts = false;
472   if (!content.empty())
473   {
474     database.GetItemsForPath(content, items.GetPath(), dbItems);
475     dbItems.SetFastLookup(true);
476   }
477
478   for (int i = 0; i < items.Size(); i++)
479   {
480     CFileItemPtr pItem = items[i];
481     CFileItemPtr match;
482     if (!content.empty()) /* optical media will be stacked down, so it's path won't match the base path */
483       match = dbItems.Get(pItem->IsOpticalMediaFile() ? pItem->GetLocalMetadataPath() : pItem->GetPath());
484     if (match)
485     {
486       pItem->UpdateInfo(*match, replaceLabels);
487
488       if (stackItems)
489       {
490         if (match->m_bIsFolder)
491           pItem->SetPath(match->GetVideoInfoTag()->m_strPath);
492         else
493           pItem->SetPath(match->GetVideoInfoTag()->m_strFileNameAndPath);
494         // if we switch from a file to a folder item it means we really shouldn't be sorting files and
495         // folders separately
496         if (pItem->m_bIsFolder != match->m_bIsFolder)
497         {
498           items.SetSortIgnoreFolders(true);
499           pItem->m_bIsFolder = match->m_bIsFolder;
500         }
501       }
502     }
503     else
504     {
505       /* NOTE: Currently we GetPlayCounts on our items regardless of whether content is set
506                 as if content is set, GetItemsForPaths doesn't return anything not in the content tables.
507                 This code can be removed once the content tables are always filled */
508       if (!pItem->m_bIsFolder && !fetchedPlayCounts)
509       {
510         database.GetPlayCounts(items.GetPath(), items);
511         fetchedPlayCounts = true;
512       }
513       
514       // preferably use some information from PVR info tag if available
515       if (pItem->HasPVRRecordingInfoTag())
516         pItem->GetPVRRecordingInfoTag()->CopyClientInfo(pItem->GetVideoInfoTag());
517
518       // set the watched overlay
519       if (pItem->IsVideo())
520         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_playCount > 0);
521     }
522   }
523 }
524
525 void CGUIWindowVideoNav::UpdateButtons()
526 {
527   CGUIWindowVideoBase::UpdateButtons();
528
529   // Update object count
530   int iItems = m_vecItems->Size();
531   if (iItems)
532   {
533     // check for parent dir and "all" items
534     // should always be the first two items
535     for (int i = 0; i <= (iItems>=2 ? 1 : 0); i++)
536     {
537       CFileItemPtr pItem = m_vecItems->Get(i);
538       if (pItem->IsParentFolder()) iItems--;
539       if (StringUtils::StartsWith(pItem->GetPath(), "/-1/")) iItems--;
540     }
541     // or the last item
542     if (m_vecItems->Size() > 2 &&
543       StringUtils::StartsWith(m_vecItems->Get(m_vecItems->Size()-1)->GetPath(), "/-1/"))
544       iItems--;
545   }
546   CStdString items = StringUtils::Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
547   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
548
549   // set the filter label
550   CStdString strLabel;
551
552   // "Playlists"
553   if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
554     strLabel = g_localizeStrings.Get(136);
555   // "{Playlist Name}"
556   else if (m_vecItems->IsPlayList())
557   {
558     // get playlist name from path
559     CStdString strDummy;
560     URIUtils::Split(m_vecItems->GetPath(), strDummy, strLabel);
561   }
562   else if (m_vecItems->GetPath().Equals("sources://video/"))
563     strLabel = g_localizeStrings.Get(744);
564   // everything else is from a videodb:// path
565   else if (m_vecItems->IsVideoDb())
566   {
567     CVideoDatabaseDirectory dir;
568     dir.GetLabel(m_vecItems->GetPath(), strLabel);
569   }
570   else
571     strLabel = URIUtils::GetFileName(m_vecItems->GetPath());
572
573   SET_CONTROL_LABEL(CONTROL_FILTER, strLabel);
574
575   int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
576   SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE, g_localizeStrings.Get(16100 + watchMode));
577
578   SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSHOWALL, watchMode != WatchedModeAll);
579
580   SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
581
582   CONTROL_ENABLE_ON_CONDITION(CONTROL_UPDATE_LIBRARY, !m_vecItems->IsAddonsPath() && !m_vecItems->IsPlugin() && !m_vecItems->IsScript());
583 }
584
585 bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
586 {
587   bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
588   listchanged |= ApplyWatchedFilter(items);
589
590   return listchanged;
591 }
592
593 /// \brief Search for names, genres, artists, directors, and plots with search string \e strSearch in the
594 /// \brief video databases and return the found \e items
595 /// \param strSearch The search string
596 /// \param items Items Found
597 void CGUIWindowVideoNav::DoSearch(const CStdString& strSearch, CFileItemList& items)
598 {
599   CFileItemList tempItems;
600   CStdString strGenre = g_localizeStrings.Get(515); // Genre
601   CStdString strActor = g_localizeStrings.Get(20337); // Actor
602   CStdString strDirector = g_localizeStrings.Get(20339); // Director
603
604   //get matching names
605   m_database.GetMoviesByName(strSearch, tempItems);
606   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20338) + "] ", items);
607
608   m_database.GetEpisodesByName(strSearch, tempItems);
609   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20359) + "] ", items);
610
611   m_database.GetTvShowsByName(strSearch, tempItems);
612   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20364) + "] ", items);
613
614   m_database.GetMusicVideosByName(strSearch, tempItems);
615   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20391) + "] ", items);
616
617   m_database.GetMusicVideosByAlbum(strSearch, tempItems);
618   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(558) + "] ", items);
619   
620   // get matching genres
621   m_database.GetMovieGenresByName(strSearch, tempItems);
622   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20342) + "] ", items);
623
624   m_database.GetTvShowGenresByName(strSearch, tempItems);
625   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20343) + "] ", items);
626
627   m_database.GetMusicVideoGenresByName(strSearch, tempItems);
628   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20389) + "] ", items);
629
630   //get actors/artists
631   m_database.GetMovieActorsByName(strSearch, tempItems);
632   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20342) + "] ", items);
633
634   m_database.GetTvShowsActorsByName(strSearch, tempItems);
635   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20343) + "] ", items);
636
637   m_database.GetMusicVideoArtistsByName(strSearch, tempItems);
638   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20389) + "] ", items);
639
640   //directors
641   m_database.GetMovieDirectorsByName(strSearch, tempItems);
642   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20342) + "] ", items);
643
644   m_database.GetTvShowsDirectorsByName(strSearch, tempItems);
645   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20343) + "] ", items);
646
647   m_database.GetMusicVideoDirectorsByName(strSearch, tempItems);
648   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20389) + "] ", items);
649
650   //plot
651   m_database.GetEpisodesByPlot(strSearch, tempItems);
652   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20365) + "] ", items);
653
654   m_database.GetMoviesByPlot(strSearch, tempItems);
655   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20323) + "] ", items);
656 }
657
658 void CGUIWindowVideoNav::PlayItem(int iItem)
659 {
660   // unlike additemtoplaylist, we need to check the items here
661   // before calling it since the current playlist will be stopped
662   // and cleared!
663
664   // root is not allowed
665   if (m_vecItems->IsVirtualDirectoryRoot())
666     return;
667
668   CGUIWindowVideoBase::PlayItem(iItem);
669 }
670
671 void CGUIWindowVideoNav::OnInfo(CFileItem* pItem, ADDON::ScraperPtr& scraper)
672 {
673   m_database.Open(); // since we can be called from the music library without being inited
674   if (pItem->IsVideoDb())
675     scraper = m_database.GetScraperForPath(pItem->GetVideoInfoTag()->m_strPath);
676   else
677   {
678     CStdString strPath,strFile;
679     URIUtils::Split(pItem->GetPath(),strPath,strFile);
680     scraper = m_database.GetScraperForPath(strPath);
681   }
682   m_database.Close();
683   CGUIWindowVideoBase::OnInfo(pItem,scraper);
684 }
685
686 void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
687 {
688   if (m_vecItems->IsParentFolder())
689     return;
690
691   if (!m_vecItems->IsVideoDb() && !pItem->IsVideoDb())
692   {
693     if (!pItem->GetPath().Equals("newsmartplaylist://video") &&
694         !pItem->GetPath().Equals("special://videoplaylists/") &&
695         !pItem->GetPath().Equals("sources://video/") &&
696         !StringUtils::StartsWithNoCase(pItem->GetPath(), "newtag://"))
697       CGUIWindowVideoBase::OnDeleteItem(pItem);
698   }
699   else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") &&
700            pItem->GetPath().size() > 22 && pItem->m_bIsFolder)
701   {
702     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
703     pDialog->SetHeading(432);
704     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433),pItem->GetLabel().c_str());
705     pDialog->SetLine(1, strLabel);
706     pDialog->SetLine(2, "");;
707     pDialog->DoModal();
708     if (pDialog->IsConfirmed())
709     {
710       CFileItemList items;
711       CDirectory::GetDirectory(pItem->GetPath(),items,"",DIR_FLAG_NO_FILE_DIRS);
712       for (int i=0;i<items.Size();++i)
713         OnDeleteItem(items[i]);
714
715       CVideoDatabaseDirectory dir;
716       CQueryParams params;
717       dir.GetQueryParams(pItem->GetPath(),params);
718       m_database.DeleteSet(params.GetSetId());
719     }
720   }
721   else if (m_vecItems->GetContent() == "tags" && pItem->m_bIsFolder)
722   {
723     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
724     pDialog->SetHeading(432);
725     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433), pItem->GetLabel().c_str());
726     pDialog->SetLine(1, strLabel);
727     pDialog->SetLine(2, "");
728     pDialog->DoModal();
729     if (pDialog->IsConfirmed())
730     {
731       CVideoDatabaseDirectory dir;
732       CQueryParams params;
733       dir.GetQueryParams(pItem->GetPath(), params);
734       m_database.DeleteTag(params.GetTagId(), (VIDEODB_CONTENT_TYPE)params.GetContentType());
735     }
736   }
737   else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
738            m_vecItems->GetPath().Equals("special://videoplaylists/"))
739   {
740     pItem->m_bIsFolder = false;
741     CFileUtils::DeleteItem(pItem);
742   }
743   else
744   {
745     if (!CGUIDialogVideoInfo::DeleteVideoItem(pItem))
746       return;
747   }
748
749   CUtil::DeleteVideoDatabaseDirectoryCache();
750 }
751
752 void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
753 {
754   CFileItemPtr item;
755   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
756     item = m_vecItems->Get(itemNumber);
757
758   CGUIWindowVideoBase::GetContextButtons(itemNumber, buttons);
759
760   if (item && item->GetProperty("pluginreplacecontextitems").asBoolean())
761     return;
762
763   CVideoDatabaseDirectory dir;
764   NODE_TYPE node = dir.GetDirectoryChildType(m_vecItems->GetPath());
765
766   if (!item)
767   {
768     // nothing to do here
769   }
770   else if (m_vecItems->GetPath().Equals("sources://video/"))
771   {
772     // get the usual shares
773     CGUIDialogContextMenu::GetContextButtons("video", item, buttons);
774     // add scan button somewhere here
775     if (g_application.IsVideoScanning())
776       buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);  // Stop Scanning
777     if (!item->IsDVD() && item->GetPath() != "add" && !item->IsParentFolder() &&
778         (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
779     {
780       CVideoDatabase database;
781       database.Open();
782       ADDON::ScraperPtr info = database.GetScraperForPath(item->GetPath());
783
784       if (!g_application.IsVideoScanning())
785       {
786         if (!item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
787         {
788           if (info && info->Content() != CONTENT_NONE)
789             buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
790           else
791             buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
792         }
793       }
794
795       if (info && !g_application.IsVideoScanning())
796         buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
797     }
798   }
799   else
800   {
801     // are we in the playlists location?
802     bool inPlaylists = m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
803                        m_vecItems->GetPath().Equals("special://videoplaylists/");
804
805     if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_artist.empty())
806     {
807       CMusicDatabase database;
808       database.Open();
809       if (database.GetArtistByName(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)) > -1)
810         buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20396);
811     }
812     if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0)
813     {
814       CMusicDatabase database;
815       database.Open();
816       if (database.GetAlbumByName(item->GetVideoInfoTag()->m_strAlbum) > -1)
817         buttons.Add(CONTEXT_BUTTON_GO_TO_ALBUM, 20397);
818     }
819     if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0 &&
820         item->GetVideoInfoTag()->m_artist.size() > 0                              &&
821         item->GetVideoInfoTag()->m_strTitle.size() > 0)
822     {
823       CMusicDatabase database;
824       database.Open();
825       if (database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),
826                                                    item->GetVideoInfoTag()->m_strAlbum,
827                                                    item->GetVideoInfoTag()->m_strTitle) > -1)
828       {
829         buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20398);
830       }
831     }
832     if (!item->IsParentFolder())
833     {
834       ADDON::ScraperPtr info;
835       VIDEO::SScanSettings settings;
836       GetScraperForItem(item.get(), info, settings);
837
838       if (info && info->Content() == CONTENT_TVSHOWS)
839         buttons.Add(CONTEXT_BUTTON_INFO, item->m_bIsFolder ? 20351 : 20352);
840       else if (info && info->Content() == CONTENT_MUSICVIDEOS)
841         buttons.Add(CONTEXT_BUTTON_INFO,20393);
842       else if (info && info->Content() == CONTENT_MOVIES)
843         buttons.Add(CONTEXT_BUTTON_INFO, 13346);
844
845       // can we update the database?
846       if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser)
847       {
848         if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
849             item->GetPath() != "sources://video/" &&
850             item->GetPath() != "special://videoplaylists/" &&
851             !StringUtils::StartsWith(item->GetPath(), "newsmartplaylist://") &&
852             !StringUtils::StartsWith(item->GetPath(), "newplaylist://") &&
853             !StringUtils::StartsWith(item->GetPath(), "newtag://"))
854         {
855           if (item->m_bIsFolder)
856           {
857             // Have both options for folders since we don't know whether all childs are watched/unwatched
858             buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
859             buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
860           }
861           else
862           {
863             if (item->GetOverlayImage().Equals("OverlayWatched.png"))
864               buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
865             else
866               buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
867           }
868         }
869         if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
870            (item->GetVideoInfoTag()->m_type == "movie" ||       // movies
871             item->GetVideoInfoTag()->m_type == "tvshow" ||      // tvshows
872             item->GetVideoInfoTag()->m_type == "episode" ||     // episodes
873             item->GetVideoInfoTag()->m_type == "musicvideo" ||  // musicvideos
874             item->GetVideoInfoTag()->m_type == "tag" ||         // tags
875             item->GetVideoInfoTag()->m_type == "set"))          // sets
876         {
877           buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
878         }
879         if (node == NODE_TYPE_TITLE_TVSHOWS)
880         {
881           if (g_application.IsVideoScanning())
882             buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);
883           else
884             buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
885         }
886
887         if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
888           buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
889
890         if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
891         {
892           if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
893             buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
894           else
895             buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
896         }
897       }
898
899       if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
900       { // non-video db items, file operations are allowed
901         if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
902             CUtil::SupportsWriteFileOperations(item->GetPath())) ||
903             (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
904                          && (item->IsPlayList() || item->IsSmartPlayList())))
905         {
906           buttons.Add(CONTEXT_BUTTON_DELETE, 117);
907           buttons.Add(CONTEXT_BUTTON_RENAME, 118);
908         }
909         // add "Set/Change content" to folders
910         if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
911         {
912           if (!g_application.IsVideoScanning())
913           {
914             if (info && info->Content() != CONTENT_NONE)
915             {
916               buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
917               buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
918             }
919             else
920               buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
921           }
922         }
923       }
924       if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
925         buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
926     }
927   }
928 }
929
930 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
931 {
932   CFileItemPtr item;
933   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
934     item = m_vecItems->Get(itemNumber);
935   if (CGUIDialogContextMenu::OnContextButton("video", item, button))
936   {
937     //TODO should we search DB for entries from plugins?
938     if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
939         && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
940     {
941       OnUnAssignContent(item->GetPath(),20375,20340,20341);
942     }
943     Refresh();
944     return true;
945   }
946   switch (button)
947   {
948   case CONTEXT_BUTTON_EDIT:
949     {
950       CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
951       if (ret >= 0)
952       {
953         if (ret == CONTEXT_BUTTON_MARK_WATCHED)
954           m_viewControl.SetSelectedItem(itemNumber + 1);
955
956         Refresh(true);
957       }
958       return true;
959     }
960
961   case CONTEXT_BUTTON_SET_SEASON_ART:
962   case CONTEXT_BUTTON_SET_ACTOR_THUMB:
963   case CONTEXT_BUTTON_SET_ARTIST_THUMB:
964     {
965       std::string type = "season";
966       if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
967         type = "actor";
968       else if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
969         type = "artist";
970       
971       bool result = CGUIDialogVideoInfo::ManageVideoItemArtwork(m_vecItems->Get(itemNumber), type);
972       Refresh();
973
974       return result;
975     }
976   case CONTEXT_BUTTON_GO_TO_ARTIST:
977     {
978       CStdString strPath;
979       CMusicDatabase database;
980       database.Open();
981       strPath = StringUtils::Format("musicdb://artists/%ld/",
982                                     database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
983       g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
984       return true;
985     }
986   case CONTEXT_BUTTON_GO_TO_ALBUM:
987     {
988       CStdString strPath;
989       CMusicDatabase database;
990       database.Open();
991       strPath = StringUtils::Format("musicdb://albums/%ld/",
992                                     database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
993       g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
994       return true;
995     }
996   case CONTEXT_BUTTON_PLAY_OTHER:
997     {
998       CMusicDatabase database;
999       database.Open();
1000       CSong song;
1001       if (database.GetSong(database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum,
1002                                                                         m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1003                                                                         song))
1004       {
1005         CApplicationMessenger::Get().PlayFile(song);
1006       }
1007       return true;
1008     }
1009
1010   default:
1011     break;
1012
1013   }
1014   return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1015 }
1016
1017 bool CGUIWindowVideoNav::OnClick(int iItem)
1018 {
1019   CFileItemPtr item = m_vecItems->Get(iItem);
1020   if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1021   {
1022     CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1023     if (!CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(item, true))
1024       return true;
1025
1026     // update list
1027     Refresh(true);
1028     m_viewControl.SetSelectedItem(iItem);
1029     return true;
1030   }
1031   else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1032   {
1033     // dont allow update while scanning
1034     if (g_application.IsVideoScanning())
1035     {
1036       CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1037       return true;
1038     }
1039
1040     //Get the new title
1041     CStdString strTag;
1042     if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1043       return true;
1044
1045     CVideoDatabase videodb;
1046     if (!videodb.Open())
1047       return true;
1048
1049     // get the media type and convert from plural to singular (by removing the trailing "s")
1050     CStdString mediaType = item->GetPath().substr(9);
1051     mediaType = mediaType.substr(0, mediaType.size() - 1);
1052     CStdString localizedType = CGUIDialogVideoInfo::GetLocalizedVideoType(mediaType);
1053     if (localizedType.empty())
1054       return true;
1055
1056     if (!videodb.GetSingleValue("tag", "tag.idTag", videodb.PrepareSQL("tag.strTag = '%s' AND tag.idTag IN (SELECT taglinks.idTag FROM taglinks WHERE taglinks.media_type = '%s')", strTag.c_str(), mediaType.c_str())).empty())
1057     {
1058       CStdString strError = StringUtils::Format(g_localizeStrings.Get(20463), strTag.c_str());
1059       CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1060       return true;
1061     }
1062
1063     int idTag = videodb.AddTag(strTag);
1064     CFileItemList items;
1065     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1066     if (CGUIDialogVideoInfo::GetItemsForTag(strLabel, mediaType, items, idTag))
1067     {
1068       for (int index = 0; index < items.Size(); index++)
1069       {
1070         if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1071           continue;
1072
1073         videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1074       }
1075     }
1076
1077     Refresh(true);
1078     return true;
1079   }
1080
1081   return CGUIWindowVideoBase::OnClick(iItem);
1082 }
1083
1084 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1085 {
1086   if (dir.Equals("MovieGenres"))
1087     return "videodb://movies/genres/";
1088   else if (dir.Equals("MovieTitles"))
1089     return "videodb://movies/titles/";
1090   else if (dir.Equals("MovieYears"))
1091     return "videodb://movies/years/";
1092   else if (dir.Equals("MovieActors"))
1093     return "videodb://movies/actors/";
1094   else if (dir.Equals("MovieDirectors"))
1095     return "videodb://movies/directors/";
1096   else if (dir.Equals("MovieStudios"))
1097     return "videodb://movies/studios/";
1098   else if (dir.Equals("MovieSets"))
1099     return "videodb://movies/sets/";
1100   else if (dir.Equals("MovieCountries"))
1101     return "videodb://movies/countries/";
1102   else if (dir.Equals("MovieTags"))
1103     return "videodb://movies/tags/";
1104   else if (dir.Equals("Movies"))
1105     return "videodb://movies/";
1106   else if (dir.Equals("TvShowGenres"))
1107     return "videodb://tvshows/genres/";
1108   else if (dir.Equals("TvShowTitles"))
1109     return "videodb://tvshows/titles/";
1110   else if (dir.Equals("TvShowYears"))
1111     return "videodb://tvshows/years/";
1112   else if (dir.Equals("TvShowActors"))
1113     return "videodb://tvshows/actors/";
1114   else if (dir.Equals("TvShowStudios"))
1115     return "videodb://tvshows/studios/";
1116   else if (dir.Equals("TvShowTags"))
1117     return "videodb://tvshows/tags/";
1118   else if (dir.Equals("TvShows"))
1119     return "videodb://tvshows/";
1120   else if (dir.Equals("MusicVideoGenres"))
1121     return "videodb://musicvideos/genres/";
1122   else if (dir.Equals("MusicVideoTitles"))
1123     return "videodb://musicvideos/titles/";
1124   else if (dir.Equals("MusicVideoYears"))
1125     return "videodb://musicvideos/years/";
1126   else if (dir.Equals("MusicVideoArtists"))
1127     return "videodb://musicvideos/artists/";
1128   else if (dir.Equals("MusicVideoAlbums"))
1129     return "videodb://musicvideos/albums/";
1130   else if (dir.Equals("MusicVideoDirectors"))
1131     return "videodb://musicvideos/directors/";
1132   else if (dir.Equals("MusicVideoStudios"))
1133     return "videodb://musicvideos/studios/";
1134   else if (dir.Equals("MusicVideoTags"))
1135     return "videodb://musicvideos/tags/";
1136   else if (dir.Equals("MusicVideos"))
1137     return "videodb://musicvideos/";
1138   else if (dir.Equals("RecentlyAddedMovies"))
1139     return "videodb://recentlyaddedmovies/";
1140   else if (dir.Equals("RecentlyAddedEpisodes"))
1141     return "videodb://recentlyaddedepisodes/";
1142   else if (dir.Equals("RecentlyAddedMusicVideos"))
1143     return "videodb://recentlyaddedmusicvideos/";
1144   else if (dir.Equals("Files"))
1145     return "sources://video/";
1146   return CGUIWindowVideoBase::GetStartFolder(dir);
1147 }
1148
1149 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1150 {
1151   bool listchanged = false;
1152   CVideoDatabaseDirectory dir;
1153   NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1154
1155   // now filter watched items as necessary
1156   bool filterWatched=false;
1157   if (node == NODE_TYPE_EPISODES
1158   ||  node == NODE_TYPE_SEASONS
1159   ||  node == NODE_TYPE_SETS
1160   ||  node == NODE_TYPE_TAGS
1161   ||  node == NODE_TYPE_TITLE_MOVIES
1162   ||  node == NODE_TYPE_TITLE_TVSHOWS
1163   ||  node == NODE_TYPE_TITLE_MUSICVIDEOS
1164   ||  node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1165   ||  node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1166   ||  node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1167     filterWatched = true;
1168   if (!items.IsVideoDb())
1169     filterWatched = true;
1170   if (items.GetContent() == "tvshows" &&
1171      (items.IsSmartPlayList() || items.IsLibraryFolder()))
1172     node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1173
1174   int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1175
1176   for (int i = 0; i < items.Size(); i++)
1177   {
1178     CFileItemPtr item = items.Get(i);
1179
1180     if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1181     {
1182       if (watchMode == WatchedModeUnwatched)
1183         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1184       if (watchMode == WatchedModeWatched)
1185         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1186       if (watchMode == WatchedModeAll)
1187         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1188       item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1189       listchanged = true;
1190     }
1191
1192     if (filterWatched)
1193     {
1194       if((watchMode==WatchedModeWatched   && item->GetVideoInfoTag()->m_playCount== 0)
1195       || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1196       {
1197         items.Remove(i);
1198         i--;
1199         listchanged = true;
1200       }
1201     }
1202   }
1203
1204   if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1205   {
1206     // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1207     // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1208     items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1209     FormatAndSort(items);
1210   }
1211
1212   return listchanged;
1213 }