Merge pull request #3653 from ace20022/langCodes
[vuplus_xbmc] / xbmc / video / dialogs / GUIDialogVideoInfo.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 "GUIDialogVideoInfo.h"
22 #include "Application.h"
23 #include "guilib/GUIWindow.h"
24 #include "Util.h"
25 #include "guilib/GUIImage.h"
26 #include "utils/StringUtils.h"
27 #include "utils/URIUtils.h"
28 #include "video/windows/GUIWindowVideoNav.h"
29 #include "dialogs/GUIDialogFileBrowser.h"
30 #include "video/VideoInfoScanner.h"
31 #include "ApplicationMessenger.h"
32 #include "video/VideoInfoTag.h"
33 #include "guilib/GUIKeyboardFactory.h"
34 #include "guilib/GUIWindowManager.h"
35 #include "dialogs/GUIDialogOK.h"
36 #include "dialogs/GUIDialogYesNo.h"
37 #include "dialogs/GUIDialogSelect.h"
38 #include "dialogs/GUIDialogProgress.h"
39 #include "filesystem/File.h"
40 #include "FileItem.h"
41 #include "storage/MediaManager.h"
42 #include "utils/AsyncFileCopy.h"
43 #include "profiles/ProfilesManager.h"
44 #include "settings/AdvancedSettings.h"
45 #include "settings/MediaSourceSettings.h"
46 #include "settings/Settings.h"
47 #include "guilib/Key.h"
48 #include "guilib/LocalizeStrings.h"
49 #include "GUIUserMessages.h"
50 #include "TextureCache.h"
51 #include "music/MusicDatabase.h"
52 #include "URL.h"
53 #include "video/VideoThumbLoader.h"
54 #include "filesystem/Directory.h"
55 #include "filesystem/VideoDatabaseDirectory.h"
56 #ifdef HAS_UPNP
57 #include "network/upnp/UPnP.h"
58 #endif
59
60 using namespace std;
61 using namespace XFILE;
62
63 #define CONTROL_IMAGE                3
64 #define CONTROL_TEXTAREA             4
65 #define CONTROL_BTN_TRACKS           5
66 #define CONTROL_BTN_REFRESH          6
67 #define CONTROL_BTN_PLAY             8
68 #define CONTROL_BTN_RESUME           9
69 #define CONTROL_BTN_GET_THUMB       10
70 #define CONTROL_BTN_PLAY_TRAILER    11
71 #define CONTROL_BTN_GET_FANART      12
72 #define CONTROL_BTN_DIRECTOR        13
73
74 #define CONTROL_LIST                50
75
76 CGUIDialogVideoInfo::CGUIDialogVideoInfo(void)
77     : CGUIDialog(WINDOW_DIALOG_VIDEO_INFO, "DialogVideoInfo.xml")
78     , m_movieItem(new CFileItem)
79 {
80   m_bRefreshAll = true;
81   m_bRefresh = false;
82   m_hasUpdatedThumb = false;
83   m_castList = new CFileItemList;
84   m_loadType = KEEP_IN_MEMORY;
85 }
86
87 CGUIDialogVideoInfo::~CGUIDialogVideoInfo(void)
88 {
89   delete m_castList;
90 }
91
92 bool CGUIDialogVideoInfo::OnMessage(CGUIMessage& message)
93 {
94   switch ( message.GetMessage() )
95   {
96   case GUI_MSG_WINDOW_DEINIT:
97     {
98       ClearCastList();
99     }
100     break;
101
102   case GUI_MSG_CLICKED:
103     {
104       int iControl = message.GetSenderId();
105       if (iControl == CONTROL_BTN_REFRESH)
106       {
107         if (m_movieItem->GetVideoInfoTag()->m_iSeason < 0 && !m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty()) // tv show
108         {
109           bool bCanceled=false;
110           if (CGUIDialogYesNo::ShowAndGetInput(20377,20378,-1,-1,bCanceled))
111           {
112             m_bRefreshAll = true;
113             CVideoDatabase db;
114             if (db.Open())
115             {
116               db.SetPathHash(m_movieItem->GetVideoInfoTag()->m_strPath,"");
117               db.Close();
118             }
119           }
120           else
121             m_bRefreshAll = false;
122
123           if (bCanceled)
124             return false;
125         }
126         m_bRefresh = true;
127         Close();
128         return true;
129       }
130       else if (iControl == CONTROL_BTN_TRACKS)
131       {
132         m_bViewReview = !m_bViewReview;
133         Update();
134       }
135       else if (iControl == CONTROL_BTN_PLAY)
136       {
137         Play();
138       }
139       else if (iControl == CONTROL_BTN_RESUME)
140       {
141         Play(true);
142       }
143       else if (iControl == CONTROL_BTN_GET_THUMB)
144       {
145         OnGetArt();
146       }
147       else if (iControl == CONTROL_BTN_PLAY_TRAILER)
148       {
149         PlayTrailer();
150       }
151       else if (iControl == CONTROL_BTN_GET_FANART)
152       {
153         OnGetFanart();
154       }
155       else if (iControl == CONTROL_BTN_DIRECTOR)
156       {
157         CStdString strDirector = StringUtils::Join(m_movieItem->GetVideoInfoTag()->m_director, g_advancedSettings.m_videoItemSeparator);
158         OnSearch(strDirector);
159       }
160       else if (iControl == CONTROL_LIST)
161       {
162         int iAction = message.GetParam1();
163         if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
164         {
165           CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), iControl);
166           OnMessage(msg);
167           int iItem = msg.GetParam1();
168           if (iItem < 0 || iItem >= m_castList->Size())
169             break;
170           CStdString strItem = m_castList->Get(iItem)->GetLabel();
171           CStdString strFind = StringUtils::Format(" %s ",g_localizeStrings.Get(20347).c_str());
172           size_t iPos = strItem.find(strFind);
173           if (iPos == std::string::npos)
174             iPos = strItem.size();
175           CStdString tmp = strItem.substr(0, iPos);
176           OnSearch(tmp);
177         }
178       }
179     }
180     break;
181   case GUI_MSG_NOTIFY_ALL:
182     {
183       if (IsActive() && message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
184       {
185         CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
186         if (item && m_movieItem->GetPath().Equals(item->GetPath()))
187         { // Just copy over the stream details and the thumb if we don't already have one
188           if (!m_movieItem->HasArt("thumb"))
189             m_movieItem->SetArt("thumb", item->GetArt("thumb"));
190           m_movieItem->GetVideoInfoTag()->m_streamDetails = item->GetVideoInfoTag()->m_streamDetails;
191         }
192         return true;
193       }
194     }
195   }
196
197   return CGUIDialog::OnMessage(message);
198 }
199
200 void CGUIDialogVideoInfo::OnInitWindow()
201 {
202   m_bRefresh = false;
203   m_bRefreshAll = true;
204   m_hasUpdatedThumb = false;
205   m_bViewReview = true;
206
207   CVideoDatabase database;
208   ADDON::ScraperPtr scraper;
209
210   if(database.Open())
211   {
212     scraper = database.GetScraperForPath(m_movieItem->GetVideoInfoTag()->GetPath());
213     database.Close();
214   }
215
216   CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_REFRESH, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber, "xx") && scraper);
217   CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_THUMB, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber.c_str() + 2, "plugin"));
218
219   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
220   if (type == VIDEODB_CONTENT_TVSHOWS || type == VIDEODB_CONTENT_MOVIES)
221     CONTROL_ENABLE_ON_CONDITION(CONTROL_BTN_GET_FANART, (CProfilesManager::Get().GetCurrentProfile().canWriteDatabases() || g_passwordManager.bMasterUser) && !StringUtils::StartsWithNoCase(m_movieItem->GetVideoInfoTag()->m_strIMDBNumber.c_str() + 2, "plugin"));
222   else
223     CONTROL_DISABLE(CONTROL_BTN_GET_FANART);
224
225   Update();
226
227   CGUIDialog::OnInitWindow();
228 }
229
230 bool CGUIDialogVideoInfo::OnAction(const CAction &action)
231 {
232   if (action.GetID() == ACTION_SHOW_INFO)
233   {
234     Close();
235     return true;
236   }
237   return CGUIDialog::OnAction(action);
238 }
239
240 void CGUIDialogVideoInfo::SetMovie(const CFileItem *item)
241 {
242   *m_movieItem = *item;
243   // setup cast list + determine type.  We need to do this here as it makes
244   // sure that content type (among other things) is set correctly for the
245   // old fixed id labels that we have floating around (they may be using
246   // content type to determine visibility, so we'll set the wrong label)
247   ClearCastList();
248   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType();
249   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
250   { // music video
251     CMusicDatabase database;
252     database.Open();
253     const std::vector<std::string> &artists = m_movieItem->GetVideoInfoTag()->m_artist;
254     for (std::vector<std::string>::const_iterator it = artists.begin(); it != artists.end(); ++it)
255     {
256       int idArtist = database.GetArtistByName(*it);
257       CStdString thumb = database.GetArtForItem(idArtist, "artist", "thumb");
258       CFileItemPtr item(new CFileItem(*it));
259       if (!thumb.empty())
260         item->SetArt("thumb", thumb);
261       item->SetIconImage("DefaultArtist.png");
262       m_castList->Add(item);
263     }
264     m_castList->SetContent("musicvideos");
265   }
266   else
267   { // movie/show/episode
268     for (CVideoInfoTag::iCast it = m_movieItem->GetVideoInfoTag()->m_cast.begin(); it != m_movieItem->GetVideoInfoTag()->m_cast.end(); ++it)
269     {
270       CStdString character;
271       if (it->strRole.empty())
272         character = it->strName;
273       else
274         character = StringUtils::Format("%s %s %s", it->strName.c_str(), g_localizeStrings.Get(20347).c_str(), it->strRole.c_str());
275       CFileItemPtr item(new CFileItem(it->strName));
276       if (!it->thumb.empty())
277         item->SetArt("thumb", it->thumb);
278       else if (CSettings::Get().GetBool("videolibrary.actorthumbs"))
279       { // backward compatibility
280         CStdString thumb = CScraperUrl::GetThumbURL(it->thumbUrl.GetFirstThumb());
281         if (!thumb.empty())
282         {
283           item->SetArt("thumb", thumb);
284           CTextureCache::Get().BackgroundCacheImage(thumb);
285         }
286       }
287       item->SetIconImage("DefaultActor.png");
288       item->SetLabel(character);
289       m_castList->Add(item);
290     }
291     // determine type:
292     if (type == VIDEODB_CONTENT_TVSHOWS)
293     {
294       m_castList->SetContent("tvshows");
295       // special case stuff for shows (not currently retrieved from the library in filemode (ref: GetTvShowInfo vs GetTVShowsByWhere)
296       m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_premiered;
297       if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
298         m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
299       m_movieItem->SetProperty("totalepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode);
300       m_movieItem->SetProperty("numepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode); // info view has no concept of current watched/unwatched filter as we could come here from files view, but set for consistency
301       m_movieItem->SetProperty("watchedepisodes", m_movieItem->GetVideoInfoTag()->m_playCount);
302       m_movieItem->SetProperty("unwatchedepisodes", m_movieItem->GetVideoInfoTag()->m_iEpisode - m_movieItem->GetVideoInfoTag()->m_playCount);
303       m_movieItem->GetVideoInfoTag()->m_playCount = (m_movieItem->GetVideoInfoTag()->m_iEpisode == m_movieItem->GetVideoInfoTag()->m_playCount) ? 1 : 0;
304     }
305     else if (type == VIDEODB_CONTENT_EPISODES)
306     {
307       m_castList->SetContent("episodes");
308       // special case stuff for episodes (not currently retrieved from the library in filemode (ref: GetEpisodeInfo vs GetEpisodesByWhere)
309       m_movieItem->m_dateTime = m_movieItem->GetVideoInfoTag()->m_firstAired;
310       if(m_movieItem->GetVideoInfoTag()->m_iYear == 0 && m_movieItem->m_dateTime.IsValid())
311         m_movieItem->GetVideoInfoTag()->m_iYear = m_movieItem->m_dateTime.GetYear();
312       // retrieve the season thumb.
313       // TODO: should we use the thumbloader for this?
314       CVideoDatabase db;
315       if (db.Open())
316       {
317         if (m_movieItem->GetVideoInfoTag()->m_iSeason > -1)
318         {
319           int seasonID = m_movieItem->GetVideoInfoTag()->m_iIdSeason;
320           if (seasonID < 0)
321             seasonID = db.GetSeasonId(m_movieItem->GetVideoInfoTag()->m_iIdShow,
322                                       m_movieItem->GetVideoInfoTag()->m_iSeason);
323           CGUIListItem::ArtMap thumbs;
324           if (db.GetArtForItem(seasonID, "season", thumbs))
325           {
326             for (CGUIListItem::ArtMap::iterator i = thumbs.begin(); i != thumbs.end(); i++)
327               m_movieItem->SetArt("season." + i->first, i->second);
328           }
329         }
330         db.Close();
331       }
332     }
333     else if (type == VIDEODB_CONTENT_MOVIES)
334     {
335       m_castList->SetContent("movies");
336
337       // local trailers should always override non-local, so check 
338       // for a local one if the registered trailer is online
339       if (m_movieItem->GetVideoInfoTag()->m_strTrailer.empty() ||
340           URIUtils::IsInternetStream(m_movieItem->GetVideoInfoTag()->m_strTrailer))
341       {
342         CStdString localTrailer = m_movieItem->FindTrailer();
343         if (!localTrailer.empty())
344         {
345           m_movieItem->GetVideoInfoTag()->m_strTrailer = localTrailer;
346           CVideoDatabase database;
347           if(database.Open())
348           {
349             database.SetSingleValue(VIDEODB_CONTENT_MOVIES, VIDEODB_ID_TRAILER,
350                                     m_movieItem->GetVideoInfoTag()->m_iDbId,
351                                     m_movieItem->GetVideoInfoTag()->m_strTrailer);
352             database.Close();
353             CUtil::DeleteVideoDatabaseDirectoryCache();
354           }
355         }
356       }
357     }
358   }
359   CVideoThumbLoader loader;
360   loader.LoadItem(m_movieItem.get());
361 }
362
363 void CGUIDialogVideoInfo::Update()
364 {
365   // setup plot text area
366   CStdString strTmp = m_movieItem->GetVideoInfoTag()->m_strPlot;
367   if (!(!m_movieItem->GetVideoInfoTag()->m_strShowTitle.empty() && m_movieItem->GetVideoInfoTag()->m_iSeason == 0)) // dont apply to tvshows
368     if (m_movieItem->GetVideoInfoTag()->m_playCount == 0 && !CSettings::Get().GetBool("videolibrary.showunwatchedplots"))
369       strTmp = g_localizeStrings.Get(20370);
370
371   StringUtils::Trim(strTmp);
372   SetLabel(CONTROL_TEXTAREA, strTmp);
373
374   CGUIMessage msg(GUI_MSG_LABEL_BIND, GetID(), CONTROL_LIST, 0, 0, m_castList);
375   OnMessage(msg);
376
377   if (GetControl(CONTROL_BTN_TRACKS)) // if no CONTROL_BTN_TRACKS found - allow skinner full visibility control over CONTROL_TEXTAREA and CONTROL_LIST
378   {
379     if (m_bViewReview)
380     {
381       if (!m_movieItem->GetVideoInfoTag()->m_artist.empty())
382       {
383         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 133);
384       }
385       else
386       {
387         SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 206);
388       }
389
390       SET_CONTROL_HIDDEN(CONTROL_LIST);
391       SET_CONTROL_VISIBLE(CONTROL_TEXTAREA);
392     }
393     else
394     {
395       SET_CONTROL_LABEL(CONTROL_BTN_TRACKS, 207);
396
397       SET_CONTROL_HIDDEN(CONTROL_TEXTAREA);
398       SET_CONTROL_VISIBLE(CONTROL_LIST);
399     }
400   }
401
402   // Check for resumability
403   if (m_movieItem->GetVideoInfoTag()->m_resumePoint.timeInSeconds > 0.0)
404     CONTROL_ENABLE(CONTROL_BTN_RESUME);
405   else
406     CONTROL_DISABLE(CONTROL_BTN_RESUME);
407
408   CONTROL_ENABLE(CONTROL_BTN_PLAY);
409
410   // update the thumbnail
411   const CGUIControl* pControl = GetControl(CONTROL_IMAGE);
412   if (pControl)
413   {
414     CGUIImage* pImageControl = (CGUIImage*)pControl;
415     pImageControl->FreeResources();
416     pImageControl->SetFileName(m_movieItem->GetArt("thumb"));
417   }
418   // tell our GUI to completely reload all controls (as some of them
419   // are likely to have had this image in use so will need refreshing)
420   if (m_hasUpdatedThumb)
421   {
422     CGUIMessage reload(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_REFRESH_THUMBS);
423     g_windowManager.SendMessage(reload);
424   }
425 }
426
427 bool CGUIDialogVideoInfo::NeedRefresh() const
428 {
429   return m_bRefresh;
430 }
431
432 bool CGUIDialogVideoInfo::RefreshAll() const
433 {
434   return m_bRefreshAll;
435 }
436
437 /// \brief Search the current directory for a string got from the virtual keyboard
438 void CGUIDialogVideoInfo::OnSearch(CStdString& strSearch)
439 {
440   CGUIDialogProgress *progress = (CGUIDialogProgress *)g_windowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
441   if (progress)
442   {
443     progress->SetHeading(194);
444     progress->SetLine(0, strSearch);
445     progress->SetLine(1, "");
446     progress->SetLine(2, "");
447     progress->StartModal();
448     progress->Progress();
449   }
450   CFileItemList items;
451   DoSearch(strSearch, items);
452
453   if (progress)
454     progress->Close();
455
456   if (items.Size())
457   {
458     CGUIDialogSelect* pDlgSelect = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
459     pDlgSelect->Reset();
460     pDlgSelect->SetHeading(283);
461
462     for (int i = 0; i < (int)items.Size(); i++)
463     {
464       CFileItemPtr pItem = items[i];
465       pDlgSelect->Add(pItem->GetLabel());
466     }
467
468     pDlgSelect->DoModal();
469
470     int iItem = pDlgSelect->GetSelectedLabel();
471     if (iItem < 0)
472       return;
473
474     CFileItem* pSelItem = new CFileItem(*items[iItem]);
475
476     OnSearchItemFound(pSelItem);
477
478     delete pSelItem;
479   }
480   else
481   {
482     CGUIDialogOK::ShowAndGetInput(194, 284, 0, 0);
483   }
484 }
485
486 /// \brief Make the actual search for the OnSearch function.
487 /// \param strSearch The search string
488 /// \param items Items Found
489 void CGUIDialogVideoInfo::DoSearch(CStdString& strSearch, CFileItemList& items)
490 {
491   CVideoDatabase db;
492   if (!db.Open())
493     return;
494
495   CFileItemList movies;
496   db.GetMoviesByActor(strSearch, movies);
497   for (int i = 0; i < movies.Size(); ++i)
498   {
499     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle;
500     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
501       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
502     movies[i]->SetLabel(label);
503   }
504   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20338) + "] ", items);
505
506   db.GetTvShowsByActor(strSearch, movies);
507   for (int i = 0; i < movies.Size(); ++i)
508   {
509     CStdString label = movies[i]->GetVideoInfoTag()->m_strShowTitle;
510     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
511       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
512     movies[i]->SetLabel(label);
513   }
514   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20364) + "] ", items);
515
516   db.GetEpisodesByActor(strSearch, movies);
517   for (int i = 0; i < movies.Size(); ++i)
518   {
519     CStdString label = movies[i]->GetVideoInfoTag()->m_strTitle + " (" +  movies[i]->GetVideoInfoTag()->m_strShowTitle + ")";
520     movies[i]->SetLabel(label);
521   }
522   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20359) + "] ", items);
523
524   db.GetMusicVideosByArtist(strSearch, movies);
525   for (int i = 0; i < movies.Size(); ++i)
526   {
527     CStdString label = StringUtils::Join(movies[i]->GetVideoInfoTag()->m_artist, g_advancedSettings.m_videoItemSeparator) + " - " + movies[i]->GetVideoInfoTag()->m_strTitle;
528     if (movies[i]->GetVideoInfoTag()->m_iYear > 0)
529       label += StringUtils::Format(" (%i)", movies[i]->GetVideoInfoTag()->m_iYear);
530     movies[i]->SetLabel(label);
531   }
532   CGUIWindowVideoBase::AppendAndClearSearchItems(movies, "[" + g_localizeStrings.Get(20391) + "] ", items);
533   db.Close();
534 }
535
536 /// \brief React on the selected search item
537 /// \param pItem Search result item
538 void CGUIDialogVideoInfo::OnSearchItemFound(const CFileItem* pItem)
539 {
540   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
541
542   CVideoDatabase db;
543   if (!db.Open())
544     return;
545
546   CVideoInfoTag movieDetails;
547   if (type == VIDEODB_CONTENT_MOVIES)
548     db.GetMovieInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
549   if (type == VIDEODB_CONTENT_EPISODES)
550     db.GetEpisodeInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
551   if (type == VIDEODB_CONTENT_TVSHOWS)
552     db.GetTvShowInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
553   if (type == VIDEODB_CONTENT_MUSICVIDEOS)
554     db.GetMusicVideoInfo(pItem->GetPath(), movieDetails, pItem->GetVideoInfoTag()->m_iDbId);
555   db.Close();
556
557   CFileItem item(*pItem);
558   *item.GetVideoInfoTag() = movieDetails;
559   SetMovie(&item);
560   // refresh our window entirely
561   Close();
562   DoModal();
563 }
564
565 void CGUIDialogVideoInfo::ClearCastList()
566 {
567   CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), CONTROL_LIST);
568   OnMessage(msg);
569   m_castList->Clear();
570 }
571
572 void CGUIDialogVideoInfo::Play(bool resume)
573 {
574   if (!m_movieItem->GetVideoInfoTag()->m_strEpisodeGuide.empty())
575   {
576     CStdString strPath = StringUtils::Format("videodb://tvshows/titles/%i/",m_movieItem->GetVideoInfoTag()->m_iDbId);
577     Close();
578     g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV,strPath);
579     return;
580   }
581
582   CFileItem movie(*m_movieItem->GetVideoInfoTag());
583   if (m_movieItem->GetVideoInfoTag()->m_strFileNameAndPath.empty())
584     movie.SetPath(m_movieItem->GetPath());
585   CGUIWindowVideoNav* pWindow = (CGUIWindowVideoNav*)g_windowManager.GetWindow(WINDOW_VIDEO_NAV);
586   if (pWindow)
587   {
588     // close our dialog
589     Close(true);
590     if (resume)
591       movie.m_lStartOffset = STARTOFFSET_RESUME;
592     else if (!CGUIWindowVideoBase::ShowResumeMenu(movie)) 
593     {
594       // The Resume dialog was closed without any choice
595       DoModal();
596       return;
597     }
598     pWindow->PlayMovie(&movie);
599   }
600 }
601
602 string CGUIDialogVideoInfo::ChooseArtType(const CFileItem &videoItem, map<string, string> &currentArt)
603 {
604   // prompt for choice
605   CGUIDialogSelect *dialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
606   if (!dialog || !videoItem.HasVideoInfoTag())
607     return "";
608
609   CFileItemList items;
610   dialog->SetHeading(13511);
611   dialog->Reset();
612   dialog->SetUseDetails(true);
613   dialog->EnableButton(true, 13516);
614
615   CVideoDatabase db;
616   db.Open();
617
618   vector<string> artTypes = CVideoThumbLoader::GetArtTypes(videoItem.GetVideoInfoTag()->m_type);
619
620   // add in any stored art for this item that is non-empty.
621   db.GetArtForItem(videoItem.GetVideoInfoTag()->m_iDbId, videoItem.GetVideoInfoTag()->m_type, currentArt);
622   for (CGUIListItem::ArtMap::iterator i = currentArt.begin(); i != currentArt.end(); ++i)
623   {
624     if (!i->second.empty() && find(artTypes.begin(), artTypes.end(), i->first) == artTypes.end())
625       artTypes.push_back(i->first);
626   }
627
628   // add any art types that exist for other media items of the same type
629   vector<string> dbArtTypes;
630   db.GetArtTypes(videoItem.GetVideoInfoTag()->m_type, dbArtTypes);
631   for (vector<string>::const_iterator it = dbArtTypes.begin(); it != dbArtTypes.end(); it++)
632   {
633     if (find(artTypes.begin(), artTypes.end(), *it) == artTypes.end())
634       artTypes.push_back(*it);
635   }
636
637   for (vector<string>::const_iterator i = artTypes.begin(); i != artTypes.end(); ++i)
638   {
639     string type = *i;
640     CFileItemPtr item(new CFileItem(type, "false"));
641     item->SetLabel(type);
642     if (videoItem.HasArt(type))
643       item->SetArt("thumb", videoItem.GetArt(type));
644     items.Add(item);
645   }
646
647   dialog->SetItems(&items);
648   dialog->DoModal();
649
650   if (dialog->IsButtonPressed())
651   {
652     // Get the new artwork name
653     CStdString strArtworkName;
654     if (!CGUIKeyboardFactory::ShowAndGetInput(strArtworkName, g_localizeStrings.Get(13516), false))
655       return "";
656
657     return strArtworkName;
658   }
659
660   return dialog->GetSelectedItem()->GetLabel();
661 }
662
663 void CGUIDialogVideoInfo::OnGetArt()
664 {
665   map<string, string> currentArt;
666   string type = ChooseArtType(*m_movieItem, currentArt);
667   if (type.empty())
668     return; // cancelled
669
670   // TODO: this can be removed once these are unified.
671   if (type == "fanart")
672     OnGetFanart();
673   else
674   {
675     CFileItemList items;
676
677     // Current thumb
678     if (m_movieItem->HasArt(type))
679     {
680       CFileItemPtr item(new CFileItem("thumb://Current", false));
681       item->SetArt("thumb", m_movieItem->GetArt(type));
682       item->SetLabel(g_localizeStrings.Get(13512));
683       items.Add(item);
684     }
685     else if ((type == "poster" || type == "banner") && currentArt.find("thumb") != currentArt.end())
686     { // add the 'thumb' type in
687       CFileItemPtr item(new CFileItem("thumb://Thumb", false));
688       item->SetArt("thumb", currentArt["thumb"]);
689       item->SetLabel(g_localizeStrings.Get(13512));
690       items.Add(item);
691     }
692
693     // Grab the thumbnails from the web
694     vector<CStdString> thumbs;
695     int season = (m_movieItem->GetVideoInfoTag()->m_type == "season") ? m_movieItem->GetVideoInfoTag()->m_iSeason : -1;
696     m_movieItem->GetVideoInfoTag()->m_strPictureURL.GetThumbURLs(thumbs, type, season);
697
698     for (unsigned int i = 0; i < thumbs.size(); ++i)
699     {
700       CStdString strItemPath = StringUtils::Format("thumb://Remote%i", i);
701       CFileItemPtr item(new CFileItem(strItemPath, false));
702       item->SetArt("thumb", thumbs[i]);
703       item->SetIconImage("DefaultPicture.png");
704       item->SetLabel(g_localizeStrings.Get(13513));
705
706       // TODO: Do we need to clear the cached image?
707       //    CTextureCache::Get().ClearCachedImage(thumb);
708       items.Add(item);
709     }
710
711     CStdString localThumb = CVideoThumbLoader::GetLocalArt(*m_movieItem, type);
712     if (!localThumb.empty())
713     {
714       CFileItemPtr item(new CFileItem("thumb://Local", false));
715       item->SetArt("thumb", localThumb);
716       item->SetLabel(g_localizeStrings.Get(13514));
717       items.Add(item);
718     }
719     else
720     { // no local thumb exists, so we are just using the IMDb thumb or cached thumb
721       // which is probably the IMDb thumb.  These could be wrong, so allow the user
722       // to delete the incorrect thumb
723       CFileItemPtr item(new CFileItem("thumb://None", false));
724       item->SetIconImage("DefaultVideo.png");
725       item->SetLabel(g_localizeStrings.Get(13515));
726       items.Add(item);
727     }
728
729     CStdString result;
730     VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
731     AddItemPathToFileBrowserSources(sources, *m_movieItem);
732     g_mediaManager.GetLocalDrives(sources);
733     if (CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(13511), result) &&
734         result != "thumb://Current") // user didn't choose the one they have
735     {
736       CStdString newThumb;
737       if (StringUtils::StartsWith(result, "thumb://Remote"))
738       {
739         int number = atoi(result.substr(14).c_str());
740         newThumb = thumbs[number];
741       }
742       else if (result == "thumb://Thumb")
743         newThumb = currentArt["thumb"];
744       else if (result == "thumb://Local")
745         newThumb = localThumb;
746       else if (CFile::Exists(result))
747         newThumb = result;
748       else // none
749         newThumb.clear();
750
751       // update thumb in the database
752       CVideoDatabase db;
753       if (db.Open())
754       {
755         db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, type, newThumb);
756         db.Close();
757       }
758       CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
759       m_movieItem->SetArt(type, newThumb);
760       if (m_movieItem->HasProperty("set_folder_thumb"))
761       { // have a folder thumb to set as well
762         VIDEO::CVideoInfoScanner::ApplyThumbToFolder(m_movieItem->GetProperty("set_folder_thumb").asString(), newThumb);
763       }
764       m_hasUpdatedThumb = true;
765     }
766   }
767
768   // Update our screen
769   Update();
770
771   // re-open the art selection dialog as we come back from
772   // the image selection dialog
773   OnGetArt();
774 }
775
776 // Allow user to select a Fanart
777 void CGUIDialogVideoInfo::OnGetFanart()
778 {
779   CFileItemList items;
780
781   CFileItem item(*m_movieItem->GetVideoInfoTag());
782   if (item.HasArt("fanart"))
783   {
784     CFileItemPtr itemCurrent(new CFileItem("fanart://Current",false));
785     itemCurrent->SetArt("thumb", item.GetArt("fanart"));
786     itemCurrent->SetLabel(g_localizeStrings.Get(20440));
787     items.Add(itemCurrent);
788   }
789
790   // ensure the fanart is unpacked
791   m_movieItem->GetVideoInfoTag()->m_fanart.Unpack();
792
793   // Grab the thumbnails from the web
794   for (unsigned int i = 0; i < m_movieItem->GetVideoInfoTag()->m_fanart.GetNumFanarts(); i++)
795   {
796     CStdString strItemPath = StringUtils::Format("fanart://Remote%i",i);
797     CFileItemPtr item(new CFileItem(strItemPath, false));
798     CStdString thumb = m_movieItem->GetVideoInfoTag()->m_fanart.GetPreviewURL(i);
799     item->SetArt("thumb", CTextureUtils::GetWrappedThumbURL(thumb));
800     item->SetIconImage("DefaultPicture.png");
801     item->SetLabel(g_localizeStrings.Get(20441));
802
803     // TODO: Do we need to clear the cached image?
804 //    CTextureCache::Get().ClearCachedImage(thumb);
805     items.Add(item);
806   }
807
808   CStdString strLocal = item.GetLocalFanart();
809   if (!strLocal.empty())
810   {
811     CFileItemPtr itemLocal(new CFileItem("fanart://Local",false));
812     itemLocal->SetArt("thumb", strLocal);
813     itemLocal->SetLabel(g_localizeStrings.Get(20438));
814
815     // TODO: Do we need to clear the cached image?
816     CTextureCache::Get().ClearCachedImage(strLocal);
817     items.Add(itemLocal);
818   }
819   else
820   {
821     CFileItemPtr itemNone(new CFileItem("fanart://None", false));
822     itemNone->SetIconImage("DefaultVideo.png");
823     itemNone->SetLabel(g_localizeStrings.Get(20439));
824     items.Add(itemNone);
825   }
826
827   CStdString result;
828   VECSOURCES sources(*CMediaSourceSettings::Get().GetSources("video"));
829   AddItemPathToFileBrowserSources(sources, item);
830   g_mediaManager.GetLocalDrives(sources);
831   bool flip=false;
832   if (!CGUIDialogFileBrowser::ShowAndGetImage(items, sources, g_localizeStrings.Get(20437), result, &flip, 20445) || result.Equals("fanart://Current"))
833     return;   // user cancelled
834
835   if (result.Equals("fanart://Local"))
836     result = strLocal;
837
838   if (StringUtils::StartsWith(result, "fanart://Remote"))
839   {
840     int iFanart = atoi(result.substr(15).c_str());
841     // set new primary fanart, and update our database accordingly
842     m_movieItem->GetVideoInfoTag()->m_fanart.SetPrimaryFanart(iFanart);
843     CVideoDatabase db;
844     if (db.Open())
845     {
846       db.UpdateFanart(*m_movieItem, (VIDEODB_CONTENT_TYPE)m_movieItem->GetVideoContentType());
847       db.Close();
848     }
849     result = m_movieItem->GetVideoInfoTag()->m_fanart.GetImageURL();
850   }
851   else if (result.Equals("fanart://None") || !CFile::Exists(result))
852     result.clear();
853
854   // set the fanart image
855   if (flip && !result.empty())
856     result = CTextureUtils::GetWrappedImageURL(result, "", "flipped");
857   CVideoDatabase db;
858   if (db.Open())
859   {
860     db.SetArtForItem(m_movieItem->GetVideoInfoTag()->m_iDbId, m_movieItem->GetVideoInfoTag()->m_type, "fanart", result);
861     db.Close();
862   }
863
864   CUtil::DeleteVideoDatabaseDirectoryCache(); // to get them new thumbs to show
865   m_movieItem->SetArt("fanart", result);
866   m_hasUpdatedThumb = true;
867
868   // Update our screen
869   Update();
870 }
871
872 void CGUIDialogVideoInfo::PlayTrailer()
873 {
874   CFileItem item;
875   item.SetPath(m_movieItem->GetVideoInfoTag()->m_strTrailer);
876   *item.GetVideoInfoTag() = *m_movieItem->GetVideoInfoTag();
877   item.GetVideoInfoTag()->m_streamDetails.Reset();
878   item.GetVideoInfoTag()->m_strTitle = StringUtils::Format("%s (%s)",
879                                                            m_movieItem->GetVideoInfoTag()->m_strTitle.c_str(),
880                                                            g_localizeStrings.Get(20410).c_str());
881   CVideoThumbLoader::SetArt(item, m_movieItem->GetArt());
882   item.GetVideoInfoTag()->m_iDbId = -1;
883   item.GetVideoInfoTag()->m_iFileId = -1;
884
885   // Close the dialog.
886   Close(true);
887
888   if (item.IsPlayList())
889     CApplicationMessenger::Get().MediaPlay(item);
890   else
891     CApplicationMessenger::Get().PlayFile(item);
892 }
893
894 void CGUIDialogVideoInfo::SetLabel(int iControl, const CStdString &strLabel)
895 {
896   if (strLabel.empty())
897   {
898     SET_CONTROL_LABEL(iControl, 416);  // "Not available"
899   }
900   else
901   {
902     SET_CONTROL_LABEL(iControl, strLabel);
903   }
904 }
905
906 std::string CGUIDialogVideoInfo::GetThumbnail() const
907 {
908   return m_movieItem->GetArt("thumb");
909 }
910
911 void CGUIDialogVideoInfo::AddItemPathToFileBrowserSources(VECSOURCES &sources, const CFileItem &item)
912 {
913   if (!item.HasVideoInfoTag())
914     return;
915
916   CStdString itemDir = item.GetVideoInfoTag()->m_basePath;
917
918   //season
919   if (itemDir.empty())
920     itemDir = item.GetVideoInfoTag()->GetPath();
921
922   CFileItem itemTmp(itemDir, false);
923   if (itemTmp.IsVideo())
924     itemDir = URIUtils::GetParentPath(itemDir);
925
926   if (!itemDir.empty() && CDirectory::Exists(itemDir))
927   {
928     CMediaSource itemSource;
929     itemSource.strName = g_localizeStrings.Get(36041);
930     itemSource.strPath = itemDir;
931     sources.push_back(itemSource);
932   }
933 }
934
935 int CGUIDialogVideoInfo::ManageVideoItem(const CFileItemPtr &item)
936 {
937   if (item == NULL || !item->IsVideoDb() || !item->HasVideoInfoTag() || item->GetVideoInfoTag()->m_iDbId < 0)
938     return -1;
939
940   CVideoDatabase database;
941   if (!database.Open())
942     return -1;
943
944   VIDEODB_CONTENT_TYPE type = (VIDEODB_CONTENT_TYPE)item->GetVideoContentType();
945   int dbId = item->GetVideoInfoTag()->m_iDbId;
946
947   CContextButtons buttons;
948   buttons.Add(CONTEXT_BUTTON_EDIT, 16105);
949
950   if (type == VIDEODB_CONTENT_MOVIES || type == VIDEODB_CONTENT_TVSHOWS)
951     buttons.Add(CONTEXT_BUTTON_EDIT_SORTTITLE, 16107);
952
953   if (item->m_bIsFolder)
954   {
955     // Have both options for folders since we don't know whether all childs are watched/unwatched
956     buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
957     buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
958   }
959   else
960   {
961     if (item->GetOverlayImage().Equals("OverlayWatched.png"))
962       buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); //Mark as UnWatched
963     else
964       buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   //Mark as Watched
965   }
966
967   if (type == VIDEODB_CONTENT_MOVIES)
968   {
969     // only show link/unlink if there are tvshows available
970     if (database.HasContent(VIDEODB_CONTENT_TVSHOWS))
971     {
972       buttons.Add(CONTEXT_BUTTON_LINK_MOVIE, 20384);
973       if (database.IsLinkedToTvshow(dbId))
974         buttons.Add(CONTEXT_BUTTON_UNLINK_MOVIE, 20385);
975     }
976
977     // set or change movie set the movie belongs to
978     buttons.Add(CONTEXT_BUTTON_SET_MOVIESET, 20465);
979   }
980
981   if (type == VIDEODB_CONTENT_EPISODES &&
982       item->GetVideoInfoTag()->m_iBookmarkId > 0)
983     buttons.Add(CONTEXT_BUTTON_UNLINK_BOOKMARK, 20405);
984
985   bool result = false;
986   int button = CGUIDialogContextMenu::ShowAndGetChoice(buttons);
987   if (button >= 0)
988   {
989     switch ((CONTEXT_BUTTON)button)
990     {
991       case CONTEXT_BUTTON_EDIT:
992         result = UpdateVideoItemTitle(item);
993         break;
994
995       case CONTEXT_BUTTON_EDIT_SORTTITLE:
996         result = UpdateVideoItemSortTitle(item);
997         break;
998
999       case CONTEXT_BUTTON_MARK_WATCHED:
1000         result = MarkWatched(item, true);
1001         break;
1002
1003       case CONTEXT_BUTTON_MARK_UNWATCHED:
1004         result = MarkWatched(item, false);
1005         break;
1006
1007       case CONTEXT_BUTTON_LINK_MOVIE:
1008         result = LinkMovieToTvShow(item, false, database);
1009         break;
1010
1011       case CONTEXT_BUTTON_UNLINK_MOVIE:
1012         result = LinkMovieToTvShow(item, true, database);
1013         break;
1014
1015       case CONTEXT_BUTTON_SET_MOVIESET:
1016       {
1017         CFileItemPtr selectedSet;
1018         if (GetSetForMovie(item.get(), selectedSet))
1019           result = SetMovieSet(item.get(), selectedSet.get());
1020         break;
1021       }
1022
1023       case CONTEXT_BUTTON_UNLINK_BOOKMARK:
1024         database.DeleteBookMarkForEpisode(*item->GetVideoInfoTag());
1025         result = true;
1026         break;
1027
1028       default:
1029         result = false;
1030         break;
1031     }
1032   }
1033
1034   database.Close();
1035
1036   if (result)
1037     return button;
1038
1039   return -1;
1040 }
1041
1042 //Add change a title's name
1043 bool CGUIDialogVideoInfo::UpdateVideoItemTitle(const CFileItemPtr &pItem)
1044 {
1045   // dont allow update while scanning
1046   if (g_application.IsVideoScanning())
1047   {
1048     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1049     return false;
1050   }
1051
1052   CVideoDatabase database;
1053   if (!database.Open())
1054     return false;
1055
1056   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1057   CVideoInfoTag detail;
1058   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1059   if (iType == VIDEODB_CONTENT_MOVIES)
1060     database.GetMovieInfo("", detail, iDbId);
1061   else if (iType == VIDEODB_CONTENT_MOVIE_SETS)
1062     database.GetSetInfo(iDbId, detail);
1063   else if (iType == VIDEODB_CONTENT_EPISODES)
1064     database.GetEpisodeInfo(pItem->GetPath(), detail, iDbId);
1065   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1066     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1067   else if (iType == VIDEODB_CONTENT_MUSICVIDEOS)
1068     database.GetMusicVideoInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1069
1070   // get the new title
1071   if (!CGUIKeyboardFactory::ShowAndGetInput(detail.m_strTitle, g_localizeStrings.Get(16105), false))
1072     return false;
1073
1074   database.UpdateMovieTitle(iDbId, detail.m_strTitle, iType);
1075   return true;
1076 }
1077
1078 bool CGUIDialogVideoInfo::MarkWatched(const CFileItemPtr &item, bool bMark)
1079 {
1080   if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases())
1081     return false;
1082
1083   // dont allow update while scanning
1084   if (g_application.IsVideoScanning())
1085   {
1086     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1087     return false;
1088   }
1089
1090   CVideoDatabase database;
1091   if (!database.Open())
1092     return false;
1093
1094   CFileItemList items;
1095   if (item->m_bIsFolder)
1096   {
1097     CStdString strPath = item->GetPath();
1098     CDirectory::GetDirectory(strPath, items);
1099   }
1100   else
1101     items.Add(item);
1102
1103   for (int i = 0; i < items.Size(); ++i)
1104   {
1105     CFileItemPtr pTmpItem = items[i];
1106     if (pTmpItem->m_bIsFolder)
1107     {
1108       MarkWatched(pTmpItem, bMark);
1109       continue;
1110     }
1111
1112     if (pTmpItem->HasVideoInfoTag() &&
1113        ((bMark && pTmpItem->GetVideoInfoTag()->m_playCount) ||
1114         (!bMark && !pTmpItem->GetVideoInfoTag()->m_playCount)))
1115       continue;
1116
1117 #ifdef HAS_UPNP
1118     if (!URIUtils::IsUPnP(item->GetPath()) || !UPNP::CUPnP::MarkWatched(*pTmpItem, bMark))
1119 #endif
1120     {
1121       // Clear resume bookmark
1122       if (bMark)
1123         database.ClearBookMarksOfFile(pTmpItem->GetPath(), CBookmark::RESUME);
1124
1125       database.SetPlayCount(*pTmpItem, bMark ? 1 : 0);
1126     }
1127   }
1128
1129   database.Close();
1130
1131   return true;
1132 }
1133
1134 bool CGUIDialogVideoInfo::GetMoviesForSet(const CFileItem *setItem, CFileItemList &originalMovies, CFileItemList &selectedMovies)
1135 {
1136   CVideoDatabase videodb;
1137   if (!videodb.Open())
1138     return false;
1139
1140   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20457));
1141   CStdString baseDir = StringUtils::Format("videodb://movies/sets/%d", setItem->GetVideoInfoTag()->m_iDbId);
1142
1143   if (!CDirectory::GetDirectory(baseDir, originalMovies) || originalMovies.Size() <= 0) // keep a copy of the original members of the set
1144     return false;
1145
1146   CFileItemList listItems;
1147   if (!videodb.GetSortedVideos(MediaTypeMovie, "videodb://movies", SortDescription(), listItems) || listItems.Size() <= 0)
1148     return false;
1149
1150   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1151   if (dialog == NULL)
1152     return false;
1153
1154   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1155
1156   dialog->Reset();
1157   dialog->SetMultiSelection(true);
1158   dialog->SetHeading(strHeading);
1159   dialog->SetItems(&listItems);
1160   vector<int> selectedIndices;
1161   for (int i = 0; i < originalMovies.Size(); i++)
1162   {
1163     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++)
1164     {
1165       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == originalMovies[i]->GetVideoInfoTag()->m_iDbId)
1166       {
1167         selectedIndices.push_back(listIndex);
1168         break;
1169       }
1170     }
1171   }
1172   dialog->SetSelected(selectedIndices);
1173   dialog->EnableButton(true, 186);
1174   dialog->DoModal();
1175
1176   if (dialog->IsConfirmed())
1177   {
1178     selectedMovies.Copy(dialog->GetSelectedItems());
1179     return (selectedMovies.Size() > 0);
1180   }
1181   else
1182     return false;
1183 }
1184
1185 bool CGUIDialogVideoInfo::GetSetForMovie(const CFileItem *movieItem, CFileItemPtr &selectedSet)
1186 {
1187   CVideoDatabase videodb;
1188   if (!videodb.Open())
1189     return false;
1190
1191   CFileItemList listItems;
1192   CStdString baseDir = "videodb://movies/sets/";
1193   if (!CDirectory::GetDirectory(baseDir, listItems) || listItems.Size() <= 0)
1194     return false;
1195   listItems.Sort(SortByLabel, SortOrderAscending, SortAttributeIgnoreArticle);
1196
1197   int currentSetId = 0;
1198   CStdString currentSetLabel;
1199
1200   if (movieItem->GetVideoInfoTag()->m_iSetId > currentSetId)
1201   {
1202     currentSetId = movieItem->GetVideoInfoTag()->m_iSetId;
1203     currentSetLabel = videodb.GetSetById(currentSetId);
1204   }
1205
1206   if (currentSetId > 0)
1207   {
1208     // add clear item
1209     CStdString strClear = StringUtils::Format(g_localizeStrings.Get(20467), currentSetLabel.c_str());
1210     CFileItemPtr clearItem(new CFileItem(strClear));
1211     clearItem->GetVideoInfoTag()->m_iDbId = -1; // -1 will be used to clear set
1212     listItems.AddFront(clearItem, 0);
1213     // add keep current set item
1214     CStdString strKeep = StringUtils::Format(g_localizeStrings.Get(20469), currentSetLabel.c_str());
1215     CFileItemPtr keepItem(new CFileItem(strKeep));
1216     keepItem->GetVideoInfoTag()->m_iDbId = currentSetId;
1217     listItems.AddFront(keepItem, 1);
1218   }
1219
1220   CGUIDialogSelect *dialog = (CGUIDialogSelect *)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1221   if (dialog == NULL)
1222     return false;
1223
1224   CStdString strHeading = StringUtils::Format(g_localizeStrings.Get(20466));
1225   dialog->Reset();
1226   dialog->SetHeading(strHeading);
1227   dialog->SetItems(&listItems);
1228   if (currentSetId >= 0)
1229   {
1230     for (int listIndex = 0; listIndex < listItems.Size(); listIndex++) 
1231     {
1232       if (listItems.Get(listIndex)->GetVideoInfoTag()->m_iDbId == currentSetId)
1233       {
1234         dialog->SetSelected(listIndex);
1235         break;
1236       }
1237     }
1238   }
1239   dialog->EnableButton(true, 20468); // new set via button
1240   dialog->DoModal();
1241
1242   if (dialog->IsButtonPressed())
1243   { // creating new set
1244     CStdString newSetTitle;
1245     if (!CGUIKeyboardFactory::ShowAndGetInput(newSetTitle, g_localizeStrings.Get(20468), false))
1246       return false;
1247     int idSet = videodb.AddSet(newSetTitle);
1248     map<string, string> movieArt, setArt;
1249     if (!videodb.GetArtForItem(idSet, "set", setArt))
1250     {
1251       videodb.GetArtForItem(movieItem->GetVideoInfoTag()->m_iDbId, "movie", movieArt);
1252       videodb.SetArtForItem(idSet, "set", movieArt);
1253     }
1254     CFileItemPtr newSet(new CFileItem(newSetTitle));
1255     newSet->GetVideoInfoTag()->m_iDbId = idSet;
1256     selectedSet = newSet;
1257     return true;
1258   }
1259   else if (dialog->IsConfirmed())
1260   {
1261     selectedSet = dialog->GetSelectedItem();
1262     return (selectedSet != NULL);
1263   }
1264   else
1265     return false;
1266 }
1267
1268 bool CGUIDialogVideoInfo::SetMovieSet(const CFileItem *movieItem, const CFileItem *selectedSet)
1269 {
1270   CVideoDatabase videodb;
1271   if (!videodb.Open())
1272     return false;
1273
1274   videodb.SetMovieSet(movieItem->GetVideoInfoTag()->m_iDbId, selectedSet->GetVideoInfoTag()->m_iDbId);
1275   return true;
1276 }
1277
1278 bool CGUIDialogVideoInfo::UpdateVideoItemSortTitle(const CFileItemPtr &pItem)
1279 {
1280   // dont allow update while scanning
1281   if (g_application.IsVideoScanning())
1282   {
1283     CGUIDialogOK::ShowAndGetInput(257, 0, 14057, 0);
1284     return false;
1285   }
1286
1287   CVideoDatabase database;
1288   if (!database.Open())
1289     return false;
1290
1291   int iDbId = pItem->GetVideoInfoTag()->m_iDbId;
1292   CVideoInfoTag detail;
1293   VIDEODB_CONTENT_TYPE iType = (VIDEODB_CONTENT_TYPE)pItem->GetVideoContentType();
1294   if (iType == VIDEODB_CONTENT_MOVIES)
1295     database.GetMovieInfo("", detail, iDbId);
1296   else if (iType == VIDEODB_CONTENT_TVSHOWS)
1297     database.GetTvShowInfo(pItem->GetVideoInfoTag()->m_strFileNameAndPath, detail, iDbId);
1298
1299   CStdString currentTitle;
1300   if (detail.m_strSortTitle.empty())
1301     currentTitle = detail.m_strTitle;
1302   else
1303     currentTitle = detail.m_strSortTitle;
1304   
1305   // get the new sort title
1306   if (!CGUIKeyboardFactory::ShowAndGetInput(currentTitle, g_localizeStrings.Get(16107), false))
1307     return false;
1308
1309   return database.UpdateVideoSortTitle(iDbId, currentTitle, iType);
1310 }
1311
1312 bool CGUIDialogVideoInfo::LinkMovieToTvShow(const CFileItemPtr &item, bool bRemove, CVideoDatabase &database)
1313 {
1314   int dbId = item->GetVideoInfoTag()->m_iDbId;
1315
1316   CFileItemList list;
1317   if (bRemove)
1318   {
1319     vector<int> ids;
1320     if (!database.GetLinksToTvShow(dbId, ids))
1321       return false;
1322
1323     for (unsigned int i = 0; i < ids.size(); ++i)
1324     {
1325       CVideoInfoTag tag;
1326       database.GetTvShowInfo("", tag, ids[i]);
1327       CFileItemPtr show(new CFileItem(tag));
1328       list.Add(show);
1329     }
1330   }
1331   else
1332   {
1333     database.GetTvShowsNav("videodb://tvshows/titles", list);
1334
1335     // remove already linked shows
1336     vector<int> ids;
1337     if (!database.GetLinksToTvShow(dbId, ids))
1338       return false;
1339
1340     for (int i = 0; i < list.Size(); )
1341     {
1342       size_t j;
1343       for (j = 0; j < ids.size(); ++j)
1344       {
1345         if (list[i]->GetVideoInfoTag()->m_iDbId == ids[j])
1346           break;
1347       }
1348       if (j == ids.size())
1349         i++;
1350       else
1351         list.Remove(i);
1352     }
1353   }
1354
1355   int iSelectedLabel = 0;
1356   if (list.Size() > 1)
1357   {
1358     list.Sort(SortByLabel, SortOrderAscending, CSettings::Get().GetBool("filelists.ignorethewhensorting") ? SortAttributeIgnoreArticle : SortAttributeNone);
1359     CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
1360     pDialog->Reset();
1361     pDialog->SetItems(&list);
1362     pDialog->SetHeading(20356);
1363     pDialog->DoModal();
1364     iSelectedLabel = pDialog->GetSelectedLabel();
1365   }
1366
1367   if (iSelectedLabel > -1)
1368     return database.LinkMovieToTvshow(dbId, list[iSelectedLabel]->GetVideoInfoTag()->m_iDbId, bRemove);
1369
1370   return false;
1371 }