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