context menu: move "Remove from library" to "Manage..." sub menu
[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       items.SetArt("thumb", "");
288       if (node == VIDEODATABASEDIRECTORY::NODE_TYPE_EPISODES ||
289           node == NODE_TYPE_SEASONS                          ||
290           node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
291       {
292         CLog::Log(LOGDEBUG, "WindowVideoNav::GetDirectory");
293         // grab the show thumb
294         CVideoInfoTag details;
295         m_database.GetTvShowInfo("", details, params.GetTvShowId());
296         map<string, string> art;
297         if (m_database.GetArtForItem(details.m_iDbId, details.m_type, art))
298         {
299           items.AppendArt(art, "tvshow");
300           items.SetArtFallback("fanart", "tvshow.fanart");
301           if (node == NODE_TYPE_SEASONS)
302           { // set an art fallback for "thumb"
303             if (items.HasArt("tvshow.poster"))
304               items.SetArtFallback("thumb", "tvshow.poster");
305             else if (items.HasArt("tvshow.banner"))
306               items.SetArtFallback("thumb", "tvshow.banner");
307           }
308         }
309
310         // Grab fanart data
311         items.SetProperty("fanart_color1", details.m_fanart.GetColor(0));
312         items.SetProperty("fanart_color2", details.m_fanart.GetColor(1));
313         items.SetProperty("fanart_color3", details.m_fanart.GetColor(2));
314
315         // save the show description (showplot)
316         items.SetProperty("showplot", details.m_strPlot);
317
318         // the container folder thumb is the parent (i.e. season or show)
319         if (node == NODE_TYPE_EPISODES || node == NODE_TYPE_RECENTLY_ADDED_EPISODES)
320         {
321           items.SetContent("episodes");
322           // grab the season thumb as the folder thumb
323           int seasonID = m_database.GetSeasonId(details.m_iDbId, params.GetSeason());
324           CGUIListItem::ArtMap seasonArt;
325           if (m_database.GetArtForItem(seasonID, "season", seasonArt))
326           {
327             items.AppendArt(art, "season");
328             // set an art fallback for "thumb"
329             if (items.HasArt("season.poster"))
330               items.SetArtFallback("thumb", "season.poster");
331             else if (items.HasArt("season.banner"))
332               items.SetArtFallback("thumb", "season.banner");
333           }
334         }
335         else
336           items.SetContent("seasons");
337       }
338       else if (node == NODE_TYPE_TITLE_MOVIES ||
339                node == NODE_TYPE_RECENTLY_ADDED_MOVIES)
340         items.SetContent("movies");
341       else if (node == NODE_TYPE_TITLE_TVSHOWS)
342         items.SetContent("tvshows");
343       else if (node == NODE_TYPE_TITLE_MUSICVIDEOS ||
344                node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
345         items.SetContent("musicvideos");
346       else if (node == NODE_TYPE_GENRE)
347         items.SetContent("genres");
348       else if (node == NODE_TYPE_COUNTRY)
349         items.SetContent("countries");
350       else if (node == NODE_TYPE_ACTOR)
351       {
352         if (params.GetContentType() == VIDEODB_CONTENT_MUSICVIDEOS)
353           items.SetContent("artists");
354         else
355           items.SetContent("actors");
356       }
357       else if (node == NODE_TYPE_DIRECTOR)
358         items.SetContent("directors");
359       else if (node == NODE_TYPE_STUDIO)
360         items.SetContent("studios");
361       else if (node == NODE_TYPE_YEAR)
362         items.SetContent("years");
363       else if (node == NODE_TYPE_MUSICVIDEOS_ALBUM)
364         items.SetContent("albums");
365       else if (node == NODE_TYPE_SETS)
366         items.SetContent("sets");
367       else if (node == NODE_TYPE_TAGS)
368         items.SetContent("tags");
369       else
370         items.SetContent("");
371     }
372     else if (!items.IsVirtualDirectoryRoot())
373     { // load info from the database
374       CStdString label;
375       if (items.GetLabel().empty() && m_rootDir.IsSource(items.GetPath(), CMediaSourceSettings::Get().GetSources("video"), &label)) 
376         items.SetLabel(label);
377       if (!items.IsSourcesPath())
378         LoadVideoInfo(items);
379     }
380
381     CVideoDbUrl videoUrl;
382     if (videoUrl.FromString(items.GetPath()) && items.GetContent() == "tags" &&
383        !items.Contains("newtag://" + videoUrl.GetType()))
384     {
385       CFileItemPtr newTag(new CFileItem("newtag://" + videoUrl.GetType(), false));
386       newTag->SetLabel(g_localizeStrings.Get(20462));
387       newTag->SetLabelPreformated(true);
388       newTag->SetSpecialSort(SortSpecialOnTop);
389       items.Add(newTag);
390     }
391   }
392   return bResult;
393 }
394
395 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items)
396 {
397   LoadVideoInfo(items, m_database);
398 }
399
400 void CGUIWindowVideoNav::LoadVideoInfo(CFileItemList &items, CVideoDatabase &database, bool allowReplaceLabels)
401 {
402   // TODO: this could possibly be threaded as per the music info loading,
403   //       we could also cache the info
404   if (!items.GetContent().empty() && !items.IsPlugin())
405     return; // don't load for listings that have content set and weren't created from plugins
406
407   CStdString content = items.GetContent();
408   // determine content only if it isn't set
409   if (content.empty())
410   {
411     content = database.GetContentForPath(items.GetPath());
412     items.SetContent(content.empty() ? "files" : content);
413   }
414
415   /*
416     If we have a matching item in the library, so we can assign the metadata to it. In addition, we can choose
417     * whether the item is stacked down (eg in the case of folders representing a single item)
418     * whether or not we assign the library's labels to the item, or leave the item as is.
419
420     As certain users (read: certain developers) don't want either of these to occur, we compromise by stacking
421     items down only if stacking is available and enabled.
422
423     Similarly, we assign the "clean" library labels to the item only if the "Replace filenames with library titles"
424     setting is enabled.
425     */
426   const bool stackItems    = items.GetProperty("isstacked").asBoolean() || (StackingAvailable(items) && CSettings::Get().GetBool("myvideos.stackvideos"));
427   const bool replaceLabels = allowReplaceLabels && CSettings::Get().GetBool("myvideos.replacelabels");
428
429   CFileItemList dbItems;
430   /* NOTE: In the future when GetItemsForPath returns all items regardless of whether they're "in the library"
431            we won't need the fetchedPlayCounts code, and can "simply" do this directly on absense of content. */
432   bool fetchedPlayCounts = false;
433   if (!content.empty())
434   {
435     database.GetItemsForPath(content, items.GetPath(), dbItems);
436     dbItems.SetFastLookup(true);
437   }
438
439   for (int i = 0; i < items.Size(); i++)
440   {
441     CFileItemPtr pItem = items[i];
442     CFileItemPtr match;
443     if (!content.empty()) /* optical media will be stacked down, so it's path won't match the base path */
444       match = dbItems.Get(pItem->IsOpticalMediaFile() ? pItem->GetLocalMetadataPath() : pItem->GetPath());
445     if (match)
446     {
447       pItem->UpdateInfo(*match, replaceLabels);
448
449       if (stackItems)
450       {
451         if (match->m_bIsFolder)
452           pItem->SetPath(match->GetVideoInfoTag()->m_strPath);
453         else
454           pItem->SetPath(match->GetVideoInfoTag()->m_strFileNameAndPath);
455         // if we switch from a file to a folder item it means we really shouldn't be sorting files and
456         // folders separately
457         if (pItem->m_bIsFolder != match->m_bIsFolder)
458         {
459           items.SetSortIgnoreFolders(true);
460           pItem->m_bIsFolder = match->m_bIsFolder;
461         }
462       }
463     }
464     else
465     {
466       /* NOTE: Currently we GetPlayCounts on our items regardless of whether content is set
467                 as if content is set, GetItemsForPaths doesn't return anything not in the content tables.
468                 This code can be removed once the content tables are always filled */
469       if (!pItem->m_bIsFolder && !fetchedPlayCounts)
470       {
471         database.GetPlayCounts(items.GetPath(), items);
472         fetchedPlayCounts = true;
473       }
474       
475       // preferably use some information from PVR info tag if available
476       if (pItem->HasPVRRecordingInfoTag())
477         pItem->GetPVRRecordingInfoTag()->CopyClientInfo(pItem->GetVideoInfoTag());
478
479       // set the watched overlay
480       if (pItem->IsVideo())
481         pItem->SetOverlayImage(CGUIListItem::ICON_OVERLAY_UNWATCHED, pItem->HasVideoInfoTag() && pItem->GetVideoInfoTag()->m_playCount > 0);
482     }
483   }
484 }
485
486 void CGUIWindowVideoNav::UpdateButtons()
487 {
488   CGUIWindowVideoBase::UpdateButtons();
489
490   // Update object count
491   int iItems = m_vecItems->Size();
492   if (iItems)
493   {
494     // check for parent dir and "all" items
495     // should always be the first two items
496     for (int i = 0; i <= (iItems>=2 ? 1 : 0); i++)
497     {
498       CFileItemPtr pItem = m_vecItems->Get(i);
499       if (pItem->IsParentFolder()) iItems--;
500       if (StringUtils::StartsWith(pItem->GetPath(), "/-1/")) iItems--;
501     }
502     // or the last item
503     if (m_vecItems->Size() > 2 &&
504       StringUtils::StartsWith(m_vecItems->Get(m_vecItems->Size()-1)->GetPath(), "/-1/"))
505       iItems--;
506   }
507   CStdString items = StringUtils::Format("%i %s", iItems, g_localizeStrings.Get(127).c_str());
508   SET_CONTROL_LABEL(CONTROL_LABELFILES, items);
509
510   // set the filter label
511   CStdString strLabel;
512
513   // "Playlists"
514   if (m_vecItems->GetPath().Equals("special://videoplaylists/"))
515     strLabel = g_localizeStrings.Get(136);
516   // "{Playlist Name}"
517   else if (m_vecItems->IsPlayList())
518   {
519     // get playlist name from path
520     CStdString strDummy;
521     URIUtils::Split(m_vecItems->GetPath(), strDummy, strLabel);
522   }
523   else if (m_vecItems->GetPath().Equals("sources://video/"))
524     strLabel = g_localizeStrings.Get(744);
525   // everything else is from a videodb:// path
526   else if (m_vecItems->IsVideoDb())
527   {
528     CVideoDatabaseDirectory dir;
529     dir.GetLabel(m_vecItems->GetPath(), strLabel);
530   }
531   else
532     strLabel = URIUtils::GetFileName(m_vecItems->GetPath());
533
534   SET_CONTROL_LABEL(CONTROL_FILTER, strLabel);
535
536   int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
537   SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE, g_localizeStrings.Get(16100 + watchMode));
538
539   SET_CONTROL_SELECTED(GetID(), CONTROL_BTNSHOWALL, watchMode != WatchedModeAll);
540
541   SET_CONTROL_SELECTED(GetID(),CONTROL_BTNPARTYMODE, g_partyModeManager.IsEnabled());
542
543   CONTROL_ENABLE_ON_CONDITION(CONTROL_UPDATE_LIBRARY, !m_vecItems->IsAddonsPath() && !m_vecItems->IsPlugin() && !m_vecItems->IsScript());
544 }
545
546 bool CGUIWindowVideoNav::GetFilteredItems(const CStdString &filter, CFileItemList &items)
547 {
548   bool listchanged = CGUIMediaWindow::GetFilteredItems(filter, items);
549   listchanged |= ApplyWatchedFilter(items);
550
551   return listchanged;
552 }
553
554 /// \brief Search for names, genres, artists, directors, and plots with search string \e strSearch in the
555 /// \brief video databases and return the found \e items
556 /// \param strSearch The search string
557 /// \param items Items Found
558 void CGUIWindowVideoNav::DoSearch(const CStdString& strSearch, CFileItemList& items)
559 {
560   CFileItemList tempItems;
561   CStdString strGenre = g_localizeStrings.Get(515); // Genre
562   CStdString strActor = g_localizeStrings.Get(20337); // Actor
563   CStdString strDirector = g_localizeStrings.Get(20339); // Director
564   CStdString strMovie = g_localizeStrings.Get(20338); // Movie
565
566   //get matching names
567   m_database.GetMoviesByName(strSearch, tempItems);
568   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20338) + "] ", items);
569
570   m_database.GetEpisodesByName(strSearch, tempItems);
571   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20359) + "] ", items);
572
573   m_database.GetTvShowsByName(strSearch, tempItems);
574   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20364) + "] ", items);
575
576   m_database.GetMusicVideosByName(strSearch, tempItems);
577   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20391) + "] ", items);
578
579   m_database.GetMusicVideosByAlbum(strSearch, tempItems);
580   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(558) + "] ", items);
581   
582   // get matching genres
583   m_database.GetMovieGenresByName(strSearch, tempItems);
584   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20342) + "] ", items);
585
586   m_database.GetTvShowGenresByName(strSearch, tempItems);
587   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20343) + "] ", items);
588
589   m_database.GetMusicVideoGenresByName(strSearch, tempItems);
590   AppendAndClearSearchItems(tempItems, "[" + strGenre + " - " + g_localizeStrings.Get(20389) + "] ", items);
591
592   //get actors/artists
593   m_database.GetMovieActorsByName(strSearch, tempItems);
594   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20342) + "] ", items);
595
596   m_database.GetTvShowsActorsByName(strSearch, tempItems);
597   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20343) + "] ", items);
598
599   m_database.GetMusicVideoArtistsByName(strSearch, tempItems);
600   AppendAndClearSearchItems(tempItems, "[" + strActor + " - " + g_localizeStrings.Get(20389) + "] ", items);
601
602   //directors
603   m_database.GetMovieDirectorsByName(strSearch, tempItems);
604   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20342) + "] ", items);
605
606   m_database.GetTvShowsDirectorsByName(strSearch, tempItems);
607   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20343) + "] ", items);
608
609   m_database.GetMusicVideoDirectorsByName(strSearch, tempItems);
610   AppendAndClearSearchItems(tempItems, "[" + strDirector + " - " + g_localizeStrings.Get(20389) + "] ", items);
611
612   //plot
613   m_database.GetEpisodesByPlot(strSearch, tempItems);
614   AppendAndClearSearchItems(tempItems, "[" + g_localizeStrings.Get(20365) + "] ", items);
615
616   m_database.GetMoviesByPlot(strSearch, tempItems);
617   AppendAndClearSearchItems(tempItems, "[" + strMovie + " " + g_localizeStrings.Get(207) + "] ", items);
618 }
619
620 void CGUIWindowVideoNav::PlayItem(int iItem)
621 {
622   // unlike additemtoplaylist, we need to check the items here
623   // before calling it since the current playlist will be stopped
624   // and cleared!
625
626   // root is not allowed
627   if (m_vecItems->IsVirtualDirectoryRoot())
628     return;
629
630   CGUIWindowVideoBase::PlayItem(iItem);
631 }
632
633 void CGUIWindowVideoNav::OnInfo(CFileItem* pItem, ADDON::ScraperPtr& scraper)
634 {
635   m_database.Open(); // since we can be called from the music library without being inited
636   if (pItem->IsVideoDb())
637     scraper = m_database.GetScraperForPath(pItem->GetVideoInfoTag()->m_strPath);
638   else
639   {
640     CStdString strPath,strFile;
641     URIUtils::Split(pItem->GetPath(),strPath,strFile);
642     scraper = m_database.GetScraperForPath(strPath);
643   }
644   m_database.Close();
645   CGUIWindowVideoBase::OnInfo(pItem,scraper);
646 }
647
648 void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem)
649 {
650   if (m_vecItems->IsParentFolder())
651     return;
652
653   if (!m_vecItems->IsVideoDb() && !pItem->IsVideoDb())
654   {
655     if (!pItem->GetPath().Equals("newsmartplaylist://video") &&
656         !pItem->GetPath().Equals("special://videoplaylists/") &&
657         !pItem->GetPath().Equals("sources://video/") &&
658         !StringUtils::StartsWithNoCase(pItem->GetPath(), "newtag://"))
659       CGUIWindowVideoBase::OnDeleteItem(pItem);
660   }
661   else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") &&
662            pItem->GetPath().size() > 22 && pItem->m_bIsFolder)
663   {
664     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
665     pDialog->SetHeading(432);
666     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433),pItem->GetLabel().c_str());
667     pDialog->SetLine(1, strLabel);
668     pDialog->SetLine(2, "");;
669     pDialog->DoModal();
670     if (pDialog->IsConfirmed())
671     {
672       CFileItemList items;
673       CDirectory::GetDirectory(pItem->GetPath(),items,"",DIR_FLAG_NO_FILE_DIRS);
674       for (int i=0;i<items.Size();++i)
675         OnDeleteItem(items[i]);
676
677       CVideoDatabaseDirectory dir;
678       CQueryParams params;
679       dir.GetQueryParams(pItem->GetPath(),params);
680       m_database.DeleteSet(params.GetSetId());
681     }
682   }
683   else if (m_vecItems->GetContent() == "tags" && pItem->m_bIsFolder)
684   {
685     CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
686     pDialog->SetHeading(432);
687     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(433), pItem->GetLabel().c_str());
688     pDialog->SetLine(1, strLabel);
689     pDialog->SetLine(2, "");
690     pDialog->DoModal();
691     if (pDialog->IsConfirmed())
692     {
693       CVideoDatabaseDirectory dir;
694       CQueryParams params;
695       dir.GetQueryParams(pItem->GetPath(), params);
696       m_database.DeleteTag(params.GetTagId(), (VIDEODB_CONTENT_TYPE)params.GetContentType());
697     }
698   }
699   else if (m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
700            m_vecItems->GetPath().Equals("special://videoplaylists/"))
701   {
702     pItem->m_bIsFolder = false;
703     CFileUtils::DeleteItem(pItem);
704   }
705   else
706   {
707     if (!CGUIDialogVideoInfo::DeleteVideoItem(pItem))
708       return;
709   }
710
711   CUtil::DeleteVideoDatabaseDirectoryCache();
712 }
713
714 void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &buttons)
715 {
716   CFileItemPtr item;
717   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
718     item = m_vecItems->Get(itemNumber);
719
720   CGUIWindowVideoBase::GetContextButtons(itemNumber, buttons);
721
722   if (item && item->GetProperty("pluginreplacecontextitems").asBoolean())
723     return;
724
725   CVideoDatabaseDirectory dir;
726   NODE_TYPE node = dir.GetDirectoryChildType(m_vecItems->GetPath());
727
728   if (!item)
729   {
730     // nothing to do here
731   }
732   else if (m_vecItems->GetPath().Equals("sources://video/"))
733   {
734     // get the usual shares
735     CGUIDialogContextMenu::GetContextButtons("video", item, buttons);
736     // add scan button somewhere here
737     if (g_application.IsVideoScanning())
738       buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);  // Stop Scanning
739     if (!item->IsDVD() && item->GetPath() != "add" && !item->IsParentFolder() &&
740         (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser))
741     {
742       CVideoDatabase database;
743       database.Open();
744       ADDON::ScraperPtr info = database.GetScraperForPath(item->GetPath());
745
746       if (!g_application.IsVideoScanning())
747       {
748         if (!item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
749         {
750           if (info && info->Content() != CONTENT_NONE)
751             buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
752           else
753             buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
754         }
755       }
756
757       if (info && !g_application.IsVideoScanning())
758         buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
759     }
760   }
761   else
762   {
763     // are we in the playlists location?
764     bool inPlaylists = m_vecItems->GetPath().Equals(CUtil::VideoPlaylistsLocation()) ||
765                        m_vecItems->GetPath().Equals("special://videoplaylists/");
766
767     if (item->HasVideoInfoTag() && !item->GetVideoInfoTag()->m_artist.empty())
768     {
769       CMusicDatabase database;
770       database.Open();
771       if (database.GetArtistByName(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)) > -1)
772         buttons.Add(CONTEXT_BUTTON_GO_TO_ARTIST, 20396);
773     }
774     if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0)
775     {
776       CMusicDatabase database;
777       database.Open();
778       if (database.GetAlbumByName(item->GetVideoInfoTag()->m_strAlbum) > -1)
779         buttons.Add(CONTEXT_BUTTON_GO_TO_ALBUM, 20397);
780     }
781     if (item->HasVideoInfoTag() && item->GetVideoInfoTag()->m_strAlbum.size() > 0 &&
782         item->GetVideoInfoTag()->m_artist.size() > 0                              &&
783         item->GetVideoInfoTag()->m_strTitle.size() > 0)
784     {
785       CMusicDatabase database;
786       database.Open();
787       if (database.GetSongByArtistAndAlbumAndTitle(StringUtils::Join(item->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator),
788                                                    item->GetVideoInfoTag()->m_strAlbum,
789                                                    item->GetVideoInfoTag()->m_strTitle) > -1)
790       {
791         buttons.Add(CONTEXT_BUTTON_PLAY_OTHER, 20398);
792       }
793     }
794     if (!item->IsParentFolder())
795     {
796       ADDON::ScraperPtr info;
797       VIDEO::SScanSettings settings;
798       GetScraperForItem(item.get(), info, settings);
799
800       if (info && info->Content() == CONTENT_TVSHOWS)
801         buttons.Add(CONTEXT_BUTTON_INFO, item->m_bIsFolder ? 20351 : 20352);
802       else if (info && info->Content() == CONTENT_MUSICVIDEOS)
803         buttons.Add(CONTEXT_BUTTON_INFO,20393);
804       else if (info && info->Content() == CONTENT_MOVIES)
805         buttons.Add(CONTEXT_BUTTON_INFO, 13346);
806
807       // can we update the database?
808       if (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser)
809       {
810         if (node == NODE_TYPE_TITLE_TVSHOWS)
811         {
812           if (g_application.IsVideoScanning())
813             buttons.Add(CONTEXT_BUTTON_STOP_SCANNING, 13353);
814           else
815             buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
816         }
817         if (!item->IsPlugin() && !item->IsScript() && !item->IsLiveTV() && !item->IsAddonsPath() &&
818             item->GetPath() != "sources://video/" &&
819             item->GetPath() != "special://videoplaylists/" &&
820             !StringUtils::StartsWith(item->GetPath(), "newsmartplaylist://") &&
821             !StringUtils::StartsWith(item->GetPath(), "newplaylist://") &&
822             !StringUtils::StartsWith(item->GetPath(), "newtag://"))
823         {
824           if (item->m_bIsFolder)
825           {
826             // Have both options for folders since we don't know whether all childs are watched/unwatched
827             buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
828             buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
829           }
830           else
831           {
832             if (item->GetOverlayImage().Equals("OverlayWatched.png"))
833               buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
834             else
835               buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
836           }
837         }
838         if (!g_application.IsVideoScanning() && item->IsVideoDb() && item->HasVideoInfoTag() &&
839            (!item->m_bIsFolder || m_vecItems->GetContent().Equals("movies") || m_vecItems->GetContent().Equals("tvshows")))
840         {
841           buttons.Add(CONTEXT_BUTTON_EDIT, 16106);
842         }
843
844         if (node == NODE_TYPE_SEASONS && item->m_bIsFolder)
845           buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511);
846
847         if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets
848         {
849           buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511);
850           buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465);
851           buttons.Add(CONTEXT_BUTTON_DELETE, 646);
852         }
853
854         if (m_vecItems->GetContent() == "tags" && item->m_bIsFolder) // tags
855         {
856           CVideoDbUrl videoUrl;
857           if (videoUrl.FromString(item->GetPath()))
858           {
859             std::string mediaType = videoUrl.GetItemType();
860
861             CStdString strLabelAdd = StringUtils::Format(g_localizeStrings.Get(20460), GetLocalizedType(videoUrl.GetItemType()).c_str());
862             CStdString strLabelRemove = StringUtils::Format(g_localizeStrings.Get(20461), GetLocalizedType(videoUrl.GetItemType()).c_str());
863             buttons.Add(CONTEXT_BUTTON_TAGS_ADD_ITEMS, strLabelAdd);
864             buttons.Add(CONTEXT_BUTTON_TAGS_REMOVE_ITEMS, strLabelRemove);
865             buttons.Add(CONTEXT_BUTTON_DELETE, 646);
866           }
867         }
868
869         if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder)
870         {
871           if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids
872             buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359);
873           else
874             buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403);
875         }
876       }
877
878       if (!m_vecItems->IsVideoDb() && !m_vecItems->IsVirtualDirectoryRoot())
879       { // non-video db items, file operations are allowed
880         if ((CSettings::Get().GetBool("filelists.allowfiledeletion") &&
881             CUtil::SupportsWriteFileOperations(item->GetPath())) ||
882             (inPlaylists && !URIUtils::GetFileName(item->GetPath()).Equals("PartyMode-Video.xsp")
883                          && (item->IsPlayList() || item->IsSmartPlayList())))
884         {
885           buttons.Add(CONTEXT_BUTTON_DELETE, 117);
886           buttons.Add(CONTEXT_BUTTON_RENAME, 118);
887         }
888         // add "Set/Change content" to folders
889         if (item->m_bIsFolder && !item->IsVideoDb() && !item->IsPlayList() && !item->IsSmartPlayList() && !item->IsLibraryFolder() && !item->IsLiveTV() && !item->IsPlugin() && !item->IsAddonsPath() && !URIUtils::IsUPnP(item->GetPath()))
890         {
891           if (!g_application.IsVideoScanning())
892           {
893             if (info && info->Content() != CONTENT_NONE)
894             {
895               buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20442);
896               buttons.Add(CONTEXT_BUTTON_SCAN, 13349);
897             }
898             else
899               buttons.Add(CONTEXT_BUTTON_SET_CONTENT, 20333);
900           }
901         }
902       }
903       if (item->IsPlugin() || item->IsScript() || m_vecItems->IsPlugin())
904         buttons.Add(CONTEXT_BUTTON_PLUGIN_SETTINGS, 1045);
905     }
906   }
907 }
908
909 // predicate used by sorting and set_difference
910 bool compFileItemsByDbId(const CFileItemPtr& lhs, const CFileItemPtr& rhs) 
911 {
912   return lhs->HasVideoInfoTag() && rhs->HasVideoInfoTag() && lhs->GetVideoInfoTag()->m_iDbId < rhs->GetVideoInfoTag()->m_iDbId;
913 }
914
915 bool CGUIWindowVideoNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
916 {
917   CFileItemPtr item;
918   if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
919     item = m_vecItems->Get(itemNumber);
920   if (CGUIDialogContextMenu::OnContextButton("video", item, button))
921   {
922     //TODO should we search DB for entries from plugins?
923     if (button == CONTEXT_BUTTON_REMOVE_SOURCE && !item->IsPlugin()
924         && !item->IsLiveTV() &&!item->IsRSS() && !URIUtils::IsUPnP(item->GetPath()))
925     {
926       OnUnAssignContent(item->GetPath(),20375,20340,20341);
927     }
928     Refresh();
929     return true;
930   }
931   switch (button)
932   {
933   case CONTEXT_BUTTON_EDIT:
934     {
935       CONTEXT_BUTTON ret = (CONTEXT_BUTTON)CGUIDialogVideoInfo::ManageVideoItem(item);
936       if (ret >= 0)
937       {
938         if (ret == CONTEXT_BUTTON_MARK_WATCHED)
939           m_viewControl.SetSelectedItem(itemNumber + 1);
940
941         Refresh(true);
942       }
943       return true;
944     }
945
946   case CONTEXT_BUTTON_SET_SEASON_ART:
947   case CONTEXT_BUTTON_SET_ACTOR_THUMB:
948   case CONTEXT_BUTTON_SET_ARTIST_THUMB:
949   case CONTEXT_BUTTON_SET_MOVIESET_ART:
950     {
951       // Grab the thumbnails from the web
952       CFileItemList items;
953       CFileItemPtr noneitem(new CFileItem("thumb://None", false));
954       CStdString currentThumb;
955       int idArtist = -1;
956       CStdString artistPath;
957       string artType = "thumb";
958       if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
959       {
960         CMusicDatabase database;
961         database.Open();
962         idArtist = database.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel());
963         database.GetArtistPath(idArtist, artistPath);
964         currentThumb = database.GetArtForItem(idArtist, "artist", "thumb");
965         if (currentThumb.empty())
966           currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
967       }
968       else if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
969         currentThumb = m_database.GetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType);
970       else
971       { // SEASON, SET
972         map<string, string> currentArt;
973         artType = CGUIDialogVideoInfo::ChooseArtType(*m_vecItems->Get(itemNumber), currentArt);
974         if (artType.empty())
975           return false;
976
977         if (artType == "fanart")
978         {
979           OnChooseFanart(*m_vecItems->Get(itemNumber));
980           return true;
981         }
982
983         if (currentArt.find(artType) != currentArt.end())
984           currentThumb = currentArt[artType];
985         else if ((artType == "poster" || artType == "banner") && currentArt.find("thumb") != currentArt.end())
986           currentThumb = currentArt["thumb"];
987       }
988       if (!currentThumb.empty())
989       {
990         CFileItemPtr item(new CFileItem("thumb://Current", false));
991         item->SetArt("thumb", currentThumb);
992         item->SetLabel(g_localizeStrings.Get(13512));
993         items.Add(item);
994       }
995       noneitem->SetIconImage("DefaultFolder.png");
996       noneitem->SetLabel(g_localizeStrings.Get(13515));
997
998       vector<CStdString> thumbs;
999       if (button != CONTEXT_BUTTON_SET_ARTIST_THUMB)
1000       {
1001         CVideoInfoTag tag;
1002         if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1003           m_database.GetTvShowInfo("",tag,m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iIdShow);
1004         else
1005           tag = *m_vecItems->Get(itemNumber)->GetVideoInfoTag();
1006         if (button == CONTEXT_BUTTON_SET_SEASON_ART)
1007           tag.m_strPictureURL.GetThumbURLs(thumbs, artType, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iSeason);
1008         else
1009           tag.m_strPictureURL.GetThumbURLs(thumbs, artType);
1010
1011         for (unsigned int i = 0; i < thumbs.size(); i++)
1012         {
1013           CStdString strItemPath = StringUtils::Format("thumb://Remote%i",i);
1014           CFileItemPtr item(new CFileItem(strItemPath, false));
1015           item->SetArt("thumb", thumbs[i]);
1016           item->SetIconImage("DefaultPicture.png");
1017           item->SetLabel(g_localizeStrings.Get(13513));
1018           items.Add(item);
1019
1020           // TODO: Do we need to clear the cached image?
1021           //    CTextureCache::Get().ClearCachedImage(thumbs[i]);
1022         }
1023       }
1024
1025       bool local=false;
1026       if (button == CONTEXT_BUTTON_SET_ARTIST_THUMB)
1027       {
1028         CStdString strThumb = URIUtils::AddFileToFolder(artistPath, "folder.jpg");
1029         if (XFILE::CFile::Exists(strThumb))
1030         {
1031           CFileItemPtr pItem(new CFileItem(strThumb,false));
1032           pItem->SetLabel(g_localizeStrings.Get(13514));
1033           pItem->SetArt("thumb", strThumb);
1034           items.Add(pItem);
1035           local = true;
1036         }
1037         else
1038           noneitem->SetIconImage("DefaultArtist.png");
1039       }
1040
1041       if (button == CONTEXT_BUTTON_SET_ACTOR_THUMB)
1042       {
1043         CStdString picturePath;
1044         CStdString strThumb = URIUtils::AddFileToFolder(picturePath, "folder.jpg");
1045         if (XFILE::CFile::Exists(strThumb))
1046         {
1047           CFileItemPtr pItem(new CFileItem(strThumb,false));
1048           pItem->SetLabel(g_localizeStrings.Get(13514));
1049           pItem->SetArt("thumb", strThumb);
1050           items.Add(pItem);
1051           local = true;
1052         }
1053         else
1054           noneitem->SetIconImage("DefaultActor.png");
1055       }
1056
1057       if (button == CONTEXT_BUTTON_SET_MOVIESET_ART)
1058         noneitem->SetIconImage("DefaultVideo.png");
1059
1060       if (!local)
1061         items.Add(noneitem);
1062
1063       VECSOURCES sources=*CMediaSourceSettings::Get().GetSources("video");
1064       g_mediaManager.GetLocalDrives(sources);
1065       CStdString result;
1066       CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, *item);
1067       if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources,
1068                                                   g_localizeStrings.Get(13511), result))
1069       {
1070         return false;   // user cancelled
1071       }
1072
1073       if (result == "thumb://Current")
1074         result = currentThumb;   // user chose the one they have
1075
1076       // delete the thumbnail if that's what the user wants, else overwrite with the
1077       // new thumbnail
1078       if (StringUtils::StartsWith(result, "thumb://Remote"))
1079       {
1080         int number = atoi(result.substr(14).c_str());
1081         result = thumbs[number];
1082       }
1083       else if (result == "thumb://None")
1084         result.clear();
1085       if (button == CONTEXT_BUTTON_SET_MOVIESET_ART ||
1086           button == CONTEXT_BUTTON_SET_ACTOR_THUMB ||
1087           button == CONTEXT_BUTTON_SET_SEASON_ART ||
1088          (button == CONTEXT_BUTTON_SET_ARTIST_THUMB && idArtist < 0))
1089         m_database.SetArtForItem(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_iDbId, m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_type, artType, result);
1090       else
1091       {
1092         CMusicDatabase db;
1093         if (db.Open())
1094           db.SetArtForItem(idArtist, "artist", artType, result);
1095       }
1096
1097       CUtil::DeleteVideoDatabaseDirectoryCache();
1098       CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
1099       g_windowManager.SendMessage(msg);
1100       Refresh();
1101
1102       return true;
1103     }
1104   case CONTEXT_BUTTON_TAGS_ADD_ITEMS:
1105     {
1106       CVideoDbUrl videoUrl;
1107       if (!videoUrl.FromString(item->GetPath()))
1108         return false;
1109       
1110       std::string mediaType = videoUrl.GetItemType();
1111       mediaType = mediaType.substr(0, mediaType.length() - 1);
1112
1113       CFileItemList items;
1114       CStdString localizedType = GetLocalizedType(mediaType);
1115       CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1116       if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId))
1117         return true;
1118
1119       CVideoDatabase videodb;
1120       if (!videodb.Open())
1121         return true;
1122
1123       for (int index = 0; index < items.Size(); index++)
1124       {
1125         if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1126           continue;
1127
1128         videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1129       }
1130
1131       // we need to clear any cached version of this tag's listing
1132       items.SetPath(item->GetPath());
1133       items.RemoveDiscCache(GetID());
1134       return true;
1135     }
1136   case CONTEXT_BUTTON_TAGS_REMOVE_ITEMS:
1137     {
1138       CVideoDbUrl videoUrl;
1139       if (!videoUrl.FromString(item->GetPath()))
1140         return false;
1141       
1142       std::string mediaType = videoUrl.GetItemType();
1143       mediaType = mediaType.substr(0, mediaType.length() - 1);
1144
1145       CFileItemList items;
1146       CStdString localizedType = GetLocalizedType(mediaType);
1147       CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1148       if (!GetItemsForTag(strLabel, mediaType, items, item->GetVideoInfoTag()->m_iDbId, false))
1149         return true;
1150
1151       CVideoDatabase videodb;
1152       if (!videodb.Open())
1153         return true;
1154
1155       for (int index = 0; index < items.Size(); index++)
1156       {
1157         if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1158           continue;
1159
1160         videodb.RemoveTagFromItem(items[index]->GetVideoInfoTag()->m_iDbId, item->GetVideoInfoTag()->m_iDbId, mediaType);
1161       }
1162
1163       // we need to clear any cached version of this tag's listing
1164       items.SetPath(item->GetPath());
1165       items.RemoveDiscCache(GetID());
1166       return true;
1167     }
1168   case CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS:
1169     {
1170       CFileItemList originalItems;
1171       CFileItemList selectedItems;
1172
1173       if (!CGUIDialogVideoInfo::GetMoviesForSet(item.get(), originalItems, selectedItems) || selectedItems.Size() == 0) // need at least one item selected
1174         return true;
1175       VECFILEITEMS original = originalItems.GetList();
1176       std::sort(original.begin(), original.end(), compFileItemsByDbId);
1177       VECFILEITEMS selected = selectedItems.GetList();
1178       std::sort(selected.begin(), selected.end(), compFileItemsByDbId);
1179
1180       bool refreshNeeded = false;
1181       // update the "added" items
1182       VECFILEITEMS addedItems;
1183       set_difference(selected.begin(),selected.end(), original.begin(),original.end(), std::back_inserter(addedItems), compFileItemsByDbId);
1184       for (VECFILEITEMS::iterator it = addedItems.begin();  it != addedItems.end(); ++it)
1185       {
1186         if (CGUIDialogVideoInfo::SetMovieSet(it->get(), item.get()))
1187           refreshNeeded = true;
1188       }
1189       // update the "deleted" items
1190       CFileItemPtr clearItem(new CFileItem());
1191       clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1192       VECFILEITEMS deletedItems;
1193       set_difference(original.begin(),original.end(), selected.begin(),selected.end(), std::back_inserter(deletedItems), compFileItemsByDbId);
1194       for (VECFILEITEMS::iterator it = deletedItems.begin();  it != deletedItems.end(); ++it)
1195       {
1196         if (CGUIDialogVideoInfo::SetMovieSet(it->get(), clearItem.get()))
1197           refreshNeeded = true;
1198       }
1199
1200       // we need to clear any cached version of this tag's listing
1201       if (refreshNeeded) 
1202         Refresh();
1203       return true;
1204     }
1205   case CONTEXT_BUTTON_GO_TO_ARTIST:
1206     {
1207       CStdString strPath;
1208       CMusicDatabase database;
1209       database.Open();
1210       strPath = StringUtils::Format("musicdb://artists/%ld/",
1211                                     database.GetArtistByName(StringUtils::Join(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator)));
1212       g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1213       return true;
1214     }
1215   case CONTEXT_BUTTON_GO_TO_ALBUM:
1216     {
1217       CStdString strPath;
1218       CMusicDatabase database;
1219       database.Open();
1220       strPath = StringUtils::Format("musicdb://albums/%ld/",
1221                                     database.GetAlbumByName(m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strAlbum));
1222       g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV,strPath);
1223       return true;
1224     }
1225   case CONTEXT_BUTTON_PLAY_OTHER:
1226     {
1227       CMusicDatabase database;
1228       database.Open();
1229       CSong song;
1230       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,
1231                                                                         m_vecItems->Get(itemNumber)->GetVideoInfoTag()->m_strTitle),
1232                                                                         song))
1233       {
1234         CApplicationMessenger::Get().PlayFile(song);
1235       }
1236       return true;
1237     }
1238
1239   default:
1240     break;
1241
1242   }
1243   return CGUIWindowVideoBase::OnContextButton(itemNumber, button);
1244 }
1245
1246 void CGUIWindowVideoNav::OnChooseFanart(const CFileItem &videoItem)
1247 {
1248   if (!videoItem.HasVideoInfoTag())
1249     return;
1250
1251   CFileItem item(videoItem);
1252
1253   CFileItemList items;
1254
1255   CVideoThumbLoader loader;
1256   loader.LoadItem(&item);
1257
1258   if (item.HasArt("fanart"))
1259   {
1260     CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
1261     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
1262     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
1263     items.Add(itemCurrent);
1264   }
1265
1266   // add the none option
1267   {
1268     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
1269     itemNone->SetIconImage("DefaultVideo.png");
1270     itemNone->SetLabel(g_localizeStrings.Get(20439));
1271     items.Add(itemNone);
1272   }
1273
1274   CStdString result;
1275   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
1276   g_mediaManager.GetLocalDrives(sources);
1277   CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(sources, item);
1278   bool flip=false;
1279   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
1280     return;
1281
1282   if (result.Equals("fanart://None") || !CFile::Exists(result))
1283     result.clear();
1284   if (!result.empty() && flip)
1285     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
1286
1287   // update the db
1288   CVideoDatabase db;
1289   if (db.Open())
1290   {
1291     db.SetArtForItem(item.GetVideoInfoTag()->m_iDbId, item.GetVideoInfoTag()->m_type, "fanart", result);
1292     db.Close();
1293   }
1294
1295   // clear view cache and reload images
1296   CUtil::DeleteVideoDatabaseDirectoryCache();
1297
1298   Refresh();
1299 }
1300
1301 bool CGUIWindowVideoNav::OnClick(int iItem)
1302 {
1303   CFileItemPtr item = m_vecItems->Get(iItem);
1304   if (!item->m_bIsFolder && item->IsVideoDb() && !item->Exists())
1305   {
1306     CLog::Log(LOGDEBUG, "%s called on '%s' but file doesn't exist", __FUNCTION__, item->GetPath().c_str());
1307     if (!CGUIDialogVideoInfo::DeleteVideoItemFromDatabase(item, true))
1308       return true;
1309
1310     // update list
1311     Refresh(true);
1312     m_viewControl.SetSelectedItem(iItem);
1313     return true;
1314   }
1315   else if (StringUtils::StartsWithNoCase(item->GetPath(), "newtag://"))
1316   {
1317     // dont allow update while scanning
1318     if (g_application.IsVideoScanning())
1319     {
1320       CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1321       return true;
1322     }
1323
1324     //Get the new title
1325     CStdString strTag;
1326     if (!CGUIKeyboardFactory::ShowAndGetInput(strTag, g_localizeStrings.Get(20462), false))
1327       return true;
1328
1329     CVideoDatabase videodb;
1330     if (!videodb.Open())
1331       return true;
1332
1333     // get the media type and convert from plural to singular (by removing the trailing "s")
1334     CStdString mediaType = item->GetPath().substr(9);
1335     mediaType = mediaType.substr(0, mediaType.size() - 1);
1336     CStdString localizedType = GetLocalizedType(mediaType);
1337     if (localizedType.empty())
1338       return true;
1339
1340     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())
1341     {
1342       CStdString strError = StringUtils::Format(g_localizeStrings.Get(20463), strTag.c_str());
1343       CGUIDialogOK::ShowAndGetInput(20462, "", strError, "");
1344       return true;
1345     }
1346
1347     int idTag = videodb.AddTag(strTag);
1348     CFileItemList items;
1349     CStdString strLabel = StringUtils::Format(g_localizeStrings.Get(20464), localizedType.c_str());
1350     if (GetItemsForTag(strLabel, mediaType, items, idTag))
1351     {
1352       for (int index = 0; index < items.Size(); index++)
1353       {
1354         if (!items[index]->HasVideoInfoTag() || items[index]->GetVideoInfoTag()->m_iDbId <= 0)
1355           continue;
1356
1357         videodb.AddTagToItem(items[index]->GetVideoInfoTag()->m_iDbId, idTag, mediaType);
1358       }
1359     }
1360
1361     Refresh(true);
1362     return true;
1363   }
1364
1365   return CGUIWindowVideoBase::OnClick(iItem);
1366 }
1367
1368 CStdString CGUIWindowVideoNav::GetStartFolder(const CStdString &dir)
1369 {
1370   if (dir.Equals("MovieGenres"))
1371     return "videodb://movies/genres/";
1372   else if (dir.Equals("MovieTitles"))
1373     return "videodb://movies/titles/";
1374   else if (dir.Equals("MovieYears"))
1375     return "videodb://movies/years/";
1376   else if (dir.Equals("MovieActors"))
1377     return "videodb://movies/actors/";
1378   else if (dir.Equals("MovieDirectors"))
1379     return "videodb://movies/directors/";
1380   else if (dir.Equals("MovieStudios"))
1381     return "videodb://movies/studios/";
1382   else if (dir.Equals("MovieSets"))
1383     return "videodb://movies/sets/";
1384   else if (dir.Equals("MovieCountries"))
1385     return "videodb://movies/countries/";
1386   else if (dir.Equals("MovieTags"))
1387     return "videodb://movies/tags/";
1388   else if (dir.Equals("Movies"))
1389     return "videodb://movies/";
1390   else if (dir.Equals("TvShowGenres"))
1391     return "videodb://tvshows/genres/";
1392   else if (dir.Equals("TvShowTitles"))
1393     return "videodb://tvshows/titles/";
1394   else if (dir.Equals("TvShowYears"))
1395     return "videodb://tvshows/years/";
1396   else if (dir.Equals("TvShowActors"))
1397     return "videodb://tvshows/actors/";
1398   else if (dir.Equals("TvShowStudios"))
1399     return "videodb://tvshows/studios/";
1400   else if (dir.Equals("TvShowTags"))
1401     return "videodb://tvshows/tags/";
1402   else if (dir.Equals("TvShows"))
1403     return "videodb://tvshows/";
1404   else if (dir.Equals("MusicVideoGenres"))
1405     return "videodb://musicvideos/genres/";
1406   else if (dir.Equals("MusicVideoTitles"))
1407     return "videodb://musicvideos/titles/";
1408   else if (dir.Equals("MusicVideoYears"))
1409     return "videodb://musicvideos/years/";
1410   else if (dir.Equals("MusicVideoArtists"))
1411     return "videodb://musicvideos/artists/";
1412   else if (dir.Equals("MusicVideoAlbums"))
1413     return "videodb://musicvideos/albums/";
1414   else if (dir.Equals("MusicVideoDirectors"))
1415     return "videodb://musicvideos/directors/";
1416   else if (dir.Equals("MusicVideoStudios"))
1417     return "videodb://musicvideos/studios/";
1418   else if (dir.Equals("MusicVideoTags"))
1419     return "videodb://musicvideos/tags/";
1420   else if (dir.Equals("MusicVideos"))
1421     return "videodb://musicvideos/";
1422   else if (dir.Equals("RecentlyAddedMovies"))
1423     return "videodb://recentlyaddedmovies/";
1424   else if (dir.Equals("RecentlyAddedEpisodes"))
1425     return "videodb://recentlyaddedepisodes/";
1426   else if (dir.Equals("RecentlyAddedMusicVideos"))
1427     return "videodb://recentlyaddedmusicvideos/";
1428   else if (dir.Equals("Files"))
1429     return "sources://video/";
1430   return CGUIWindowVideoBase::GetStartFolder(dir);
1431 }
1432
1433 bool CGUIWindowVideoNav::ApplyWatchedFilter(CFileItemList &items)
1434 {
1435   bool listchanged = false;
1436   CVideoDatabaseDirectory dir;
1437   NODE_TYPE node = dir.GetDirectoryChildType(items.GetPath());
1438
1439   // now filter watched items as necessary
1440   bool filterWatched=false;
1441   if (node == NODE_TYPE_EPISODES
1442   ||  node == NODE_TYPE_SEASONS
1443   ||  node == NODE_TYPE_SETS
1444   ||  node == NODE_TYPE_TAGS
1445   ||  node == NODE_TYPE_TITLE_MOVIES
1446   ||  node == NODE_TYPE_TITLE_TVSHOWS
1447   ||  node == NODE_TYPE_TITLE_MUSICVIDEOS
1448   ||  node == NODE_TYPE_RECENTLY_ADDED_EPISODES
1449   ||  node == NODE_TYPE_RECENTLY_ADDED_MOVIES
1450   ||  node == NODE_TYPE_RECENTLY_ADDED_MUSICVIDEOS)
1451     filterWatched = true;
1452   if (!items.IsVideoDb())
1453     filterWatched = true;
1454   if (items.GetContent() == "tvshows" &&
1455      (items.IsSmartPlayList() || items.IsLibraryFolder()))
1456     node = NODE_TYPE_TITLE_TVSHOWS; // so that the check below works
1457
1458   int watchMode = CMediaSettings::Get().GetWatchedMode(m_vecItems->GetContent());
1459
1460   for (int i = 0; i < items.Size(); i++)
1461   {
1462     CFileItemPtr item = items.Get(i);
1463
1464     if(item->HasVideoInfoTag() && (node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS))
1465     {
1466       if (watchMode == WatchedModeUnwatched)
1467         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("unwatchedepisodes").asInteger();
1468       if (watchMode == WatchedModeWatched)
1469         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("watchedepisodes").asInteger();
1470       if (watchMode == WatchedModeAll)
1471         item->GetVideoInfoTag()->m_iEpisode = (int)item->GetProperty("totalepisodes").asInteger();
1472       item->SetProperty("numepisodes", item->GetVideoInfoTag()->m_iEpisode);
1473       listchanged = true;
1474     }
1475
1476     if (filterWatched)
1477     {
1478       if((watchMode==WatchedModeWatched   && item->GetVideoInfoTag()->m_playCount== 0)
1479       || (watchMode==WatchedModeUnwatched && item->GetVideoInfoTag()->m_playCount > 0))
1480       {
1481         items.Remove(i);
1482         i--;
1483         listchanged = true;
1484       }
1485     }
1486   }
1487
1488   if(node == NODE_TYPE_TITLE_TVSHOWS || node == NODE_TYPE_SEASONS)
1489   {
1490     // the watched filter may change the "numepisodes" property which is reflected in the TV_SHOWS and SEASONS nodes
1491     // therefore, the items labels have to be refreshed, and possibly the list needs resorting as well.
1492     items.ClearSortState(); // this is needed to force resorting even if sort method did not change
1493     FormatAndSort(items);
1494   }
1495
1496   return listchanged;
1497 }
1498
1499 bool CGUIWindowVideoNav::GetItemsForTag(const CStdString &strHeading, const std::string &type, CFileItemList &items, int idTag /* = -1 */, bool showAll /* = true */)
1500 {
1501   CVideoDatabase videodb;
1502   if (!videodb.Open())
1503     return false;
1504
1505   MediaType mediaType = MediaTypeNone;
1506   std::string baseDir = "videodb://";
1507   std::string idColumn;
1508   if (type.compare("movie") == 0)
1509   {
1510     mediaType = MediaTypeMovie;
1511     baseDir += "movies";
1512     idColumn = "idMovie";
1513   }
1514   else if (type.compare("tvshow") == 0)
1515   {
1516     mediaType = MediaTypeTvShow;
1517     baseDir += "tvshows";
1518     idColumn = "idShow";
1519   }
1520   else if (type.compare("musicvideo") == 0)
1521   {
1522     mediaType = MediaTypeMusicVideo;
1523     baseDir += "musicvideos";
1524     idColumn = "idMVideo";
1525   }
1526
1527   baseDir += "/titles/";
1528   CVideoDbUrl videoUrl;
1529   if (!videoUrl.FromString(baseDir))
1530     return false;
1531
1532   CVideoDatabase::Filter filter;
1533   if (idTag > 0)
1534   {
1535     if (!showAll)
1536       videoUrl.AddOption("tagid", idTag);
1537     else
1538       filter.where = videodb.PrepareSQL("%sview.%s NOT IN (SELECT taglinks.idMedia FROM taglinks WHERE taglinks.idTag = %d AND taglinks.media_type = '%s')", type.c_str(), idColumn.c_str(), idTag, type.c_str());
1539   }
1540
1541   CFileItemList listItems;
1542   if (!videodb.GetSortedVideos(mediaType, videoUrl.ToString(), SortDescription(), listItems, filter) || listItems.Size() <= 0)
1543     return false;
1544
1545   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1546   if (dialog == NULL)
1547     return false;
1548
1549   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1550
1551   dialog->Reset();
1552   dialog->SetMultiSelection(true);
1553   dialog->SetHeading(strHeading);
1554   dialog->SetItems(&listItems);
1555   dialog->EnableButton(true, 186);
1556   dialog->DoModal();
1557
1558   items.Copy(dialog->GetSelectedItems());
1559   return items.Size() > 0;
1560 }
1561
1562 CStdString CGUIWindowVideoNav::GetLocalizedType(const std::string &strType)
1563 {
1564   if (strType == "movie" || strType == "movies")
1565     return g_localizeStrings.Get(20342);
1566   else if (strType == "tvshow" || strType == "tvshows")
1567     return g_localizeStrings.Get(20343);
1568   else if (strType == "episode" || strType == "episodes")
1569     return g_localizeStrings.Get(20359);
1570   else if (strType == "musicvideo" || strType == "musicvideos")
1571     return g_localizeStrings.Get(20391);
1572   else
1573     return "";
1574 }