e415725151cef3bf2f70bc0408c67cefe8d463fd
[vuplus_xbmc] / xbmc / view / GUIViewState.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 "view/GUIViewState.h"
22 #include "pvr/windows/GUIViewStatePVR.h"
23 #include "addons/GUIViewStateAddonBrowser.h"
24 #include "music/GUIViewStateMusic.h"
25 #include "video/GUIViewStateVideo.h"
26 #include "pictures/GUIViewStatePictures.h"
27 #include "profiles/ProfilesManager.h"
28 #include "programs/GUIViewStatePrograms.h"
29 #include "PlayListPlayer.h"
30 #include "utils/URIUtils.h"
31 #include "URL.h"
32 #include "GUIPassword.h"
33 #include "ViewDatabase.h"
34 #include "AutoSwitch.h"
35 #include "guilib/GUIWindowManager.h"
36 #include "addons/Addon.h"
37 #include "addons/AddonManager.h"
38 #include "addons/PluginSource.h"
39 #include "view/ViewState.h"
40 #include "settings/AdvancedSettings.h"
41 #include "settings/MediaSourceSettings.h"
42 #include "settings/Settings.h"
43 #include "FileItem.h"
44 #include "guilib/Key.h"
45 #include "filesystem/AddonsDirectory.h"
46 #include "guilib/TextureManager.h"
47
48 #if defined(TARGET_ANDROID)
49 #include "filesystem/AndroidAppDirectory.h"
50 #endif
51
52 #define PROPERTY_SORT_ORDER         "sort.order"
53 #define PROPERTY_SORT_ASCENDING     "sort.ascending"
54
55 using namespace std;
56 using namespace ADDON;
57 using namespace PVR;
58
59 CStdString CGUIViewState::m_strPlaylistDirectory;
60 VECSOURCES CGUIViewState::m_sources;
61
62 CGUIViewState* CGUIViewState::GetViewState(int windowId, const CFileItemList& items)
63 {
64   // don't expect derived classes to clear the sources
65   m_sources.clear();
66
67   if (windowId == 0)
68     return GetViewState(g_windowManager.GetActiveWindow(),items);
69
70   const CURL url=items.GetAsUrl();
71
72   if (items.IsAddonsPath())
73     return new CGUIViewStateAddonBrowser(items);
74
75   if (items.HasSortDetails())
76     return new CGUIViewStateFromItems(items);
77
78   if (url.GetProtocol()=="musicdb")
79     return new CGUIViewStateMusicDatabase(items);
80
81   if (url.GetProtocol()=="musicsearch")
82     return new CGUIViewStateMusicSearch(items);
83
84   if (items.IsSmartPlayList() || url.GetProtocol() == "upnp" ||
85       items.IsLibraryFolder())
86   {
87     if (items.GetContent() == "songs" ||
88         items.GetContent() == "albums" ||
89         items.GetContent() == "mixed")
90       return new CGUIViewStateMusicSmartPlaylist(items);
91     else if (items.GetContent() == "musicvideos")
92       return new CGUIViewStateVideoMusicVideos(items);
93     else if (items.GetContent() == "tvshows")
94       return new CGUIViewStateVideoTVShows(items);
95     else if (items.GetContent() == "episodes")
96       return new CGUIViewStateVideoEpisodes(items);
97     else if (items.GetContent() == "movies")
98       return new CGUIViewStateVideoMovies(items);
99   }
100
101   if (url.GetProtocol() == "library")
102     return new CGUIViewStateLibrary(items);
103
104   if (items.IsPlayList())
105     return new CGUIViewStateMusicPlaylist(items);
106
107   if (items.GetPath() == "special://musicplaylists/")
108     return new CGUIViewStateWindowMusicSongs(items);
109
110   if (url.GetProtocol() == "androidapp")
111     return new CGUIViewStateWindowPrograms(items);
112
113   if (windowId==WINDOW_MUSIC_NAV)
114     return new CGUIViewStateWindowMusicNav(items);
115
116   if (windowId==WINDOW_MUSIC_FILES)
117     return new CGUIViewStateWindowMusicSongs(items);
118
119   if (windowId==WINDOW_MUSIC_PLAYLIST)
120     return new CGUIViewStateWindowMusicPlaylist(items);
121
122   if (windowId==WINDOW_MUSIC_PLAYLIST_EDITOR)
123     return new CGUIViewStateWindowMusicSongs(items);
124
125   if (windowId==WINDOW_VIDEO_FILES)
126     return new CGUIViewStateWindowVideoFiles(items);
127
128   if (windowId==WINDOW_VIDEO_NAV)
129     return new CGUIViewStateWindowVideoNav(items);
130
131   if (windowId==WINDOW_VIDEO_PLAYLIST)
132     return new CGUIViewStateWindowVideoPlaylist(items);
133
134   if (windowId==WINDOW_PVR)
135     return new CGUIViewStatePVR(items);
136
137   if (windowId==WINDOW_PICTURES)
138     return new CGUIViewStateWindowPictures(items);
139
140   if (windowId==WINDOW_PROGRAMS)
141     return new CGUIViewStateWindowPrograms(items);
142   
143   if (windowId==WINDOW_ADDON_BROWSER)
144     return new CGUIViewStateAddonBrowser(items);
145
146   //  Use as fallback/default
147   return new CGUIViewStateGeneral(items);
148 }
149
150 CGUIViewState::CGUIViewState(const CFileItemList& items) : m_items(items)
151 {
152   m_currentViewAsControl=0;
153   m_currentSortMethod=0;
154   m_playlist = PLAYLIST_NONE;
155   m_sortOrder = SortOrderAscending;
156 }
157
158 CGUIViewState::~CGUIViewState()
159 {
160 }
161
162 SortOrder CGUIViewState::GetDisplaySortOrder() const
163 {
164   // we actually treat some sort orders in reverse, so that we can have
165   // the one sort order variable to save but it can be ascending usually,
166   // and descending for the views which should be usually descending.
167   // default sort order for date, size, program count + rating is reversed
168   SortDescription sorting = GetSortMethod();
169   if (sorting.sortBy == SortByDate || sorting.sortBy == SortBySize || sorting.sortBy == SortByPlaycount ||
170       sorting.sortBy == SortByRating || sorting.sortBy == SortByProgramCount ||
171       sorting.sortBy == SortByBitrate || sorting.sortBy == SortByListeners)
172   {
173     if (m_sortOrder == SortOrderAscending)
174       return SortOrderDescending;
175     if (m_sortOrder == SortOrderDescending)
176       return SortOrderAscending;
177   }
178
179   return m_sortOrder;
180 }
181
182 SortOrder CGUIViewState::SetNextSortOrder()
183 {
184   if (m_sortOrder == SortOrderAscending)
185     SetSortOrder(SortOrderDescending);
186   else
187     SetSortOrder(SortOrderAscending);
188
189   SaveViewState();
190
191   return m_sortOrder;
192 }
193
194 int CGUIViewState::GetViewAsControl() const
195 {
196   return m_currentViewAsControl;
197 }
198
199 void CGUIViewState::SetViewAsControl(int viewAsControl)
200 {
201   if (viewAsControl == DEFAULT_VIEW_AUTO)
202     m_currentViewAsControl = CAutoSwitch::GetView(m_items);
203   else
204     m_currentViewAsControl = viewAsControl;
205 }
206
207 void CGUIViewState::SaveViewAsControl(int viewAsControl)
208 {
209   SetViewAsControl(viewAsControl);
210   SaveViewState();
211 }
212
213 SortDescription CGUIViewState::GetSortMethod() const
214 {
215   SortDescription sorting;
216   if (m_currentSortMethod>=0 && m_currentSortMethod<(int)m_sortMethods.size())
217     sorting = m_sortMethods[m_currentSortMethod].m_sortDescription;
218   sorting.sortOrder = m_sortOrder;
219
220   return sorting;
221 }
222
223 bool CGUIViewState::HasMultipleSortMethods() const
224 {
225   return m_sortMethods.size() > 1;
226 }
227
228 int CGUIViewState::GetSortMethodLabel() const
229 {
230   if (m_currentSortMethod>=0 && m_currentSortMethod<(int)m_sortMethods.size())
231     return m_sortMethods[m_currentSortMethod].m_buttonLabel;
232
233   return 103; // Sort By: Name
234 }
235
236 void CGUIViewState::GetSortMethodLabelMasks(LABEL_MASKS& masks) const
237 {
238   if (m_currentSortMethod>=0 && m_currentSortMethod<(int)m_sortMethods.size())
239   {
240     masks=m_sortMethods[m_currentSortMethod].m_labelMasks;
241     return;
242   }
243
244   masks.m_strLabelFile.Empty();
245   masks.m_strLabel2File.Empty();
246   masks.m_strLabelFolder.Empty();
247   masks.m_strLabel2Folder.Empty();
248   return;
249 }
250
251 void CGUIViewState::AddSortMethod(SortBy sortBy, int buttonLabel, const LABEL_MASKS &labelMasks, SortAttribute sortAttributes /* = SortAttributeNone */)
252 {
253   AddSortMethod(sortBy, sortAttributes, buttonLabel, labelMasks);
254 }
255
256 void CGUIViewState::AddSortMethod(SortBy sortBy, SortAttribute sortAttributes, int buttonLabel, const LABEL_MASKS &labelMasks)
257 {
258   for (size_t i = 0; i < m_sortMethods.size(); ++i)
259     if (m_sortMethods[i].m_sortDescription.sortBy == sortBy)
260       return;
261
262   SORT_METHOD_DETAILS sort;
263   sort.m_sortDescription.sortBy = sortBy;
264   sort.m_sortDescription.sortAttributes = sortAttributes;
265   sort.m_buttonLabel = buttonLabel;
266   sort.m_labelMasks = labelMasks;
267   m_sortMethods.push_back(sort);
268 }
269
270 void CGUIViewState::AddSortMethod(SortDescription sortDescription, int buttonLabel, const LABEL_MASKS &labelMasks)
271 {
272   AddSortMethod(sortDescription.sortBy, sortDescription.sortAttributes, buttonLabel, labelMasks);
273 }
274
275 void CGUIViewState::SetCurrentSortMethod(int method)
276 {
277   SortBy sortBy = (SortBy)method;
278   SortAttribute sortAttributes = SortAttributeNone;
279   if (CSettings::Get().GetBool("filelists.ignorethewhensorting"))
280     sortAttributes = SortAttributeIgnoreArticle;
281
282   if (sortBy < SortByNone || sortBy > SortByRandom)
283     return; // invalid
284
285   SetSortMethod(sortBy, sortAttributes);
286   SaveViewState();
287 }
288
289 void CGUIViewState::SetSortMethod(SortBy sortBy, SortAttribute sortAttributes /* = SortAttributeNone */)
290 {
291   for (int i = 0; i < (int)m_sortMethods.size(); ++i)
292   {
293     if (m_sortMethods[i].m_sortDescription.sortBy == sortBy &&
294        // don't care about SortAttributeIgnoreFolders as it wasn't part of the old sorting comparison
295         m_sortMethods[i].m_sortDescription.sortAttributes == (sortAttributes & ~SortAttributeIgnoreFolders))
296     {
297       m_currentSortMethod = i;
298       break;
299     }
300   }
301   SetSortOrder(m_sortOrder);
302 }
303
304 void CGUIViewState::SetSortMethod(SortDescription sortDescription)
305 {
306   return SetSortMethod(sortDescription.sortBy, sortDescription.sortAttributes);
307 }
308
309 SortDescription CGUIViewState::SetNextSortMethod(int direction /* = 1 */)
310 {
311   m_currentSortMethod += direction;
312
313   if (m_currentSortMethod >= (int)m_sortMethods.size())
314     m_currentSortMethod = 0;
315   if (m_currentSortMethod < 0)
316     m_currentSortMethod = m_sortMethods.size() ? (int)m_sortMethods.size() - 1 : 0;
317   SetSortOrder(m_sortOrder);
318
319   SaveViewState();
320
321   return GetSortMethod();
322 }
323
324 bool CGUIViewState::HideExtensions()
325 {
326   return !CSettings::Get().GetBool("filelists.showextensions");
327 }
328
329 bool CGUIViewState::HideParentDirItems()
330 {
331   return !CSettings::Get().GetBool("filelists.showparentdiritems");
332 }
333
334 bool CGUIViewState::DisableAddSourceButtons()
335 {
336   if (CProfilesManager::Get().GetCurrentProfile().canWriteSources() || g_passwordManager.bMasterUser)
337     return !CSettings::Get().GetBool("filelists.showaddsourcebuttons");
338
339   return true;
340 }
341
342 int CGUIViewState::GetPlaylist()
343 {
344   return m_playlist;
345 }
346
347 const CStdString& CGUIViewState::GetPlaylistDirectory()
348 {
349   return m_strPlaylistDirectory;
350 }
351
352 void CGUIViewState::SetPlaylistDirectory(const CStdString& strDirectory)
353 {
354   m_strPlaylistDirectory=strDirectory;
355   URIUtils::RemoveSlashAtEnd(m_strPlaylistDirectory);
356 }
357
358 bool CGUIViewState::IsCurrentPlaylistDirectory(const CStdString& strDirectory)
359 {
360   if (g_playlistPlayer.GetCurrentPlaylist()!=GetPlaylist())
361     return false;
362
363   CStdString strDir=strDirectory;
364   URIUtils::RemoveSlashAtEnd(strDir);
365
366   return (m_strPlaylistDirectory==strDir);
367 }
368
369 bool CGUIViewState::AutoPlayNextItem()
370 {
371   return false;
372 }
373
374 CStdString CGUIViewState::GetLockType()
375 {
376   return "";
377 }
378
379 CStdString CGUIViewState::GetExtensions()
380 {
381   return "";
382 }
383
384 VECSOURCES& CGUIViewState::GetSources()
385 {
386   return m_sources;
387 }
388
389 void CGUIViewState::AddAddonsSource(const CStdString &content, const CStdString &label, const CStdString &thumb)
390 {
391   if (!g_advancedSettings.m_bVirtualShares)
392     return;
393
394   CFileItemList items;
395   if (XFILE::CAddonsDirectory::GetScriptsAndPlugins(content, items))
396   { // add the plugin source
397     CMediaSource source;
398     source.strPath = "addons://sources/" + content + "/";    
399     source.strName = label;
400     if (!thumb.IsEmpty() && g_TextureManager.HasTexture(thumb))
401       source.m_strThumbnailImage = thumb;
402     source.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
403     source.m_ignore = true;
404     m_sources.push_back(source);
405   }
406 }
407
408 #if defined(TARGET_ANDROID)
409 void CGUIViewState::AddAndroidSource(const CStdString &content, const CStdString &label, const CStdString &thumb)
410 {
411   CFileItemList items;
412   XFILE::CAndroidAppDirectory apps;
413   if (apps.GetDirectory(content, items))
414   {
415     CMediaSource source;
416     source.strPath = "androidapp://sources/" + content + "/";
417     source.strName = label;
418     if (!thumb.IsEmpty() && g_TextureManager.HasTexture(thumb))
419       source.m_strThumbnailImage = thumb;
420     source.m_iDriveType = CMediaSource::SOURCE_TYPE_LOCAL;
421     source.m_ignore = true;
422     m_sources.push_back(source);
423   }
424 }
425 #endif
426
427 void CGUIViewState::AddLiveTVSources()
428 {
429   VECSOURCES *sources = CMediaSourceSettings::Get().GetSources("video");
430   for (IVECSOURCES it = sources->begin(); it != sources->end(); it++)
431   {
432     if (URIUtils::IsLiveTV((*it).strPath))
433     {
434       CMediaSource source;
435       source.strPath = (*it).strPath;
436       source.strName = (*it).strName;
437       source.vecPaths = (*it).vecPaths;
438       source.m_strThumbnailImage = "";
439       source.FromNameAndPaths("video", source.strName, source.vecPaths);
440       m_sources.push_back(source);
441     }
442   }
443 }
444
445 CGUIViewStateGeneral::CGUIViewStateGeneral(const CFileItemList& items) : CGUIViewState(items)
446 {
447   AddSortMethod(SortByLabel, 551, LABEL_MASKS("%F", "%I", "%L", ""));  // Filename, size | Foldername, empty
448   SetSortMethod(SortByLabel);
449
450   SetViewAsControl(DEFAULT_VIEW_LIST);
451
452   SetSortOrder(SortOrderAscending);
453 }
454
455 void CGUIViewState::SetSortOrder(SortOrder sortOrder)
456 {
457   if (GetSortMethod().sortBy == SortByNone)
458     m_sortOrder = SortOrderNone;
459   else if (sortOrder == SortOrderNone)
460     m_sortOrder = SortOrderAscending;
461   else
462     m_sortOrder = sortOrder;
463 }
464
465 void CGUIViewState::LoadViewState(const CStdString &path, int windowID)
466 { // get our view state from the db
467   CViewDatabase db;
468   if (db.Open())
469   {
470     CViewState state;
471     if (db.GetViewState(path, windowID, state, CSettings::Get().GetString("lookandfeel.skin")) ||
472         db.GetViewState(path, windowID, state, ""))
473     {
474       SetViewAsControl(state.m_viewMode);
475       SetSortMethod(state.m_sortDescription);
476       SetSortOrder(state.m_sortDescription.sortOrder);
477     }
478     db.Close();
479   }
480 }
481
482 void CGUIViewState::SaveViewToDb(const CStdString &path, int windowID, CViewState *viewState)
483 {
484   CViewDatabase db;
485   if (db.Open())
486   {
487     SortDescription sorting = GetSortMethod();
488     CViewState state(m_currentViewAsControl, sorting.sortBy, m_sortOrder, sorting.sortAttributes);
489     if (viewState)
490       *viewState = state;
491     db.SetViewState(path, windowID, state, CSettings::Get().GetString("lookandfeel.skin"));
492     db.Close();
493     if (viewState)
494       CSettings::Get().Save();
495   }
496 }
497
498 void CGUIViewState::AddPlaylistOrder(const CFileItemList &items, LABEL_MASKS label_masks)
499 {
500   SortBy sortBy = SortByPlaylistOrder;
501   int         sortLabel = 559;
502   SortOrder   sortOrder = SortOrderAscending;
503   if (items.HasProperty(PROPERTY_SORT_ORDER))
504   {
505     sortBy = (SortBy)items.GetProperty(PROPERTY_SORT_ORDER).asInteger();
506     if (sortBy != SortByNone)
507     {
508       sortLabel = SortUtils::GetSortLabel(sortBy);
509       sortOrder = items.GetProperty(PROPERTY_SORT_ASCENDING).asBoolean() ? SortOrderAscending : SortOrderDescending;
510     }
511   }
512   AddSortMethod(sortBy, sortLabel, label_masks);
513   SetSortMethod(sortBy);
514   SetSortOrder(sortOrder);
515 }
516
517
518 CGUIViewStateFromItems::CGUIViewStateFromItems(const CFileItemList &items) : CGUIViewState(items)
519 {
520   const vector<SORT_METHOD_DETAILS> &details = items.GetSortDetails();
521   for (unsigned int i = 0; i < details.size(); i++)
522   {
523     const SORT_METHOD_DETAILS sort = details[i];
524     AddSortMethod(sort.m_sortDescription, sort.m_buttonLabel, sort.m_labelMasks);
525   }
526   // TODO: Should default sort/view mode be specified?
527   m_currentSortMethod = 0;
528
529   SetViewAsControl(DEFAULT_VIEW_LIST);
530
531   SetSortOrder(SortOrderAscending);
532   if (items.IsPlugin())
533   {
534     CURL url(items.GetPath());
535     AddonPtr addon;
536     if (CAddonMgr::Get().GetAddon(url.GetHostName(),addon) && addon)
537     {
538       PluginPtr plugin = boost::static_pointer_cast<CPluginSource>(addon);
539       if (plugin->Provides(CPluginSource::AUDIO))
540         m_playlist = PLAYLIST_MUSIC;
541       if (plugin->Provides(CPluginSource::VIDEO))
542         m_playlist = PLAYLIST_VIDEO;
543     }
544   }
545   LoadViewState(items.GetPath(), g_windowManager.GetActiveWindow());
546 }
547
548 void CGUIViewStateFromItems::SaveViewState()
549 {
550   SaveViewToDb(m_items.GetPath(), g_windowManager.GetActiveWindow());
551 }
552
553 CGUIViewStateLibrary::CGUIViewStateLibrary(const CFileItemList &items) : CGUIViewState(items)
554 {
555   AddSortMethod(SortByNone, 551, LABEL_MASKS("%F", "%I", "%L", ""));  // Filename, Size | Foldername, empty
556   SetSortMethod(SortByNone);
557   SetSortOrder(SortOrderNone);
558
559   SetViewAsControl(DEFAULT_VIEW_LIST);
560
561   LoadViewState(items.GetPath(), g_windowManager.GetActiveWindow());
562 }
563
564 void CGUIViewStateLibrary::SaveViewState()
565 {
566   SaveViewToDb(m_items.GetPath(), g_windowManager.GetActiveWindow());
567 }
568