PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/xbmc/pvr/windows/GUIWindowPVRRecordings.cpp

https://gitlab.com/freesoftware/xbmc
C++ | 409 lines | 338 code | 54 blank | 17 comment | 90 complexity | fec0904c92c292d0cd00e25bcd1dc119 MD5 | raw file
  1. /*
  2. * Copyright (C) 2012-2018 Team Kodi
  3. * This file is part of Kodi - https://kodi.tv
  4. *
  5. * SPDX-License-Identifier: GPL-2.0-or-later
  6. * See LICENSES/README.md for more information.
  7. */
  8. #include "GUIWindowPVRRecordings.h"
  9. #include "GUIInfoManager.h"
  10. #include "ServiceBroker.h"
  11. #include "guilib/GUIComponent.h"
  12. #include "guilib/GUIMessage.h"
  13. #include "guilib/GUIRadioButtonControl.h"
  14. #include "guilib/GUIWindowManager.h"
  15. #include "guilib/LocalizeStrings.h"
  16. #include "input/actions/Action.h"
  17. #include "input/actions/ActionIDs.h"
  18. #include "pvr/PVRManager.h"
  19. #include "pvr/guilib/PVRGUIActions.h"
  20. #include "pvr/recordings/PVRRecording.h"
  21. #include "pvr/recordings/PVRRecordings.h"
  22. #include "pvr/recordings/PVRRecordingsPath.h"
  23. #include "settings/MediaSettings.h"
  24. #include "settings/Settings.h"
  25. #include "settings/SettingsComponent.h"
  26. #include "utils/URIUtils.h"
  27. #include "video/VideoLibraryQueue.h"
  28. #include "video/windows/GUIWindowVideoNav.h"
  29. #include <memory>
  30. #include <mutex>
  31. #include <string>
  32. using namespace PVR;
  33. CGUIWindowPVRRecordingsBase::CGUIWindowPVRRecordingsBase(bool bRadio, int id, const std::string& xmlFile) :
  34. CGUIWindowPVRBase(bRadio, id, xmlFile),
  35. m_bShowDeletedRecordings(false),
  36. m_settings({
  37. CSettings::SETTING_PVRRECORD_GROUPRECORDINGS,
  38. CSettings::SETTING_MYVIDEOS_SELECTACTION
  39. })
  40. {
  41. }
  42. CGUIWindowPVRRecordingsBase::~CGUIWindowPVRRecordingsBase() = default;
  43. void CGUIWindowPVRRecordingsBase::OnWindowLoaded()
  44. {
  45. CONTROL_SELECT(CONTROL_BTNGROUPITEMS);
  46. }
  47. std::string CGUIWindowPVRRecordingsBase::GetDirectoryPath()
  48. {
  49. const std::string basePath = CPVRRecordingsPath(m_bShowDeletedRecordings, m_bRadio);
  50. return URIUtils::PathHasParent(m_vecItems->GetPath(), basePath) ? m_vecItems->GetPath() : basePath;
  51. }
  52. void CGUIWindowPVRRecordingsBase::GetContextButtons(int itemNumber, CContextButtons& buttons)
  53. {
  54. if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
  55. return;
  56. CFileItemPtr pItem = m_vecItems->Get(itemNumber);
  57. if (pItem->IsParentFolder())
  58. {
  59. // No context menu for ".." items
  60. return;
  61. }
  62. bool isDeletedRecording = false;
  63. std::shared_ptr<CPVRRecording> recording(pItem->GetPVRRecordingInfoTag());
  64. if (recording)
  65. {
  66. isDeletedRecording = recording->IsDeleted();
  67. if (isDeletedRecording)
  68. {
  69. if (m_vecItems->GetObjectCount() > 1)
  70. buttons.Add(CONTEXT_BUTTON_DELETE_ALL, 19292); /* Delete all permanently */
  71. }
  72. }
  73. if (!isDeletedRecording)
  74. CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons);
  75. }
  76. bool CGUIWindowPVRRecordingsBase::OnAction(const CAction& action)
  77. {
  78. if (action.GetID() == ACTION_PARENT_DIR ||
  79. action.GetID() == ACTION_NAV_BACK)
  80. {
  81. CPVRRecordingsPath path(m_vecItems->GetPath());
  82. if (path.IsValid() && !path.IsRecordingsRoot())
  83. {
  84. GoParentFolder();
  85. return true;
  86. }
  87. }
  88. else if (action.GetID() == ACTION_TOGGLE_WATCHED)
  89. {
  90. const std::shared_ptr<CFileItem> pItem = m_vecItems->Get(m_viewControl.GetSelectedItem());
  91. if (!pItem || pItem->IsParentFolder())
  92. return false;
  93. bool bUnWatched = false;
  94. if (pItem->HasPVRRecordingInfoTag())
  95. bUnWatched = pItem->GetPVRRecordingInfoTag()->GetPlayCount() == 0;
  96. else if (pItem->m_bIsFolder)
  97. bUnWatched = pItem->GetProperty("unwatchedepisodes").asInteger() > 0;
  98. else
  99. return false;
  100. CVideoLibraryQueue::GetInstance().MarkAsWatched(pItem, bUnWatched);
  101. return true;
  102. }
  103. return CGUIWindowPVRBase::OnAction(action);
  104. }
  105. bool CGUIWindowPVRRecordingsBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
  106. {
  107. if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
  108. return false;
  109. CFileItemPtr pItem = m_vecItems->Get(itemNumber);
  110. return OnContextButtonDeleteAll(pItem.get(), button) ||
  111. CGUIMediaWindow::OnContextButton(itemNumber, button);
  112. }
  113. bool CGUIWindowPVRRecordingsBase::Update(const std::string& strDirectory, bool updateFilterPath /* = true */)
  114. {
  115. m_thumbLoader.StopThread();
  116. int iOldCount = m_vecItems->GetObjectCount();
  117. const std::string oldPath = m_vecItems->GetPath();
  118. bool bReturn = CGUIWindowPVRBase::Update(strDirectory);
  119. if (bReturn)
  120. {
  121. // TODO: does it make sense to show the non-deleted recordings, although user wants
  122. // to see the deleted recordings? Or is this just another hack to avoid misbehavior
  123. // of CGUIMediaWindow if it has no content?
  124. std::unique_lock<CCriticalSection> lock(m_critSection);
  125. /* empty list for deleted recordings */
  126. if (m_vecItems->GetObjectCount() == 0 && m_bShowDeletedRecordings)
  127. {
  128. /* show the normal recordings instead */
  129. m_bShowDeletedRecordings = false;
  130. lock.unlock();
  131. Update(GetDirectoryPath());
  132. return bReturn;
  133. }
  134. }
  135. if (bReturn && iOldCount > 0 && m_vecItems->GetObjectCount() == 0 && oldPath == m_vecItems->GetPath())
  136. {
  137. /* go to the parent folder if we're in a subdirectory and for instance just deleted the last item */
  138. const CPVRRecordingsPath path(m_vecItems->GetPath());
  139. if (path.IsValid() && !path.IsRecordingsRoot())
  140. GoParentFolder();
  141. }
  142. return bReturn;
  143. }
  144. void CGUIWindowPVRRecordingsBase::UpdateButtons()
  145. {
  146. int iWatchMode = CMediaSettings::GetInstance().GetWatchedMode("recordings");
  147. int iStringId = 257; // "Error"
  148. if (iWatchMode == WatchedModeAll)
  149. iStringId = 22015; // "All recordings"
  150. else if (iWatchMode == WatchedModeUnwatched)
  151. iStringId = 16101; // "Unwatched"
  152. else if (iWatchMode == WatchedModeWatched)
  153. iStringId = 16102; // "Watched"
  154. SET_CONTROL_LABEL(CONTROL_BTNSHOWMODE, g_localizeStrings.Get(iStringId));
  155. bool bGroupRecordings = m_settings.GetBoolValue(CSettings::SETTING_PVRRECORD_GROUPRECORDINGS);
  156. SET_CONTROL_SELECTED(GetID(), CONTROL_BTNGROUPITEMS, bGroupRecordings);
  157. CGUIRadioButtonControl* btnShowDeleted = static_cast<CGUIRadioButtonControl*>(GetControl(CONTROL_BTNSHOWDELETED));
  158. if (btnShowDeleted)
  159. {
  160. btnShowDeleted->SetVisible(m_bRadio ? CServiceBroker::GetPVRManager().Recordings()->HasDeletedRadioRecordings() : CServiceBroker::GetPVRManager().Recordings()->HasDeletedTVRecordings());
  161. btnShowDeleted->SetSelected(m_bShowDeletedRecordings);
  162. }
  163. CGUIWindowPVRBase::UpdateButtons();
  164. SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, m_bShowDeletedRecordings ? g_localizeStrings.Get(19179) : ""); /* Deleted recordings trash */
  165. const CPVRRecordingsPath path(m_vecItems->GetPath());
  166. SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, bGroupRecordings && path.IsValid() ? path.GetUnescapedDirectoryPath() : "");
  167. }
  168. bool CGUIWindowPVRRecordingsBase::OnMessage(CGUIMessage& message)
  169. {
  170. bool bReturn = false;
  171. switch (message.GetMessage())
  172. {
  173. case GUI_MSG_CLICKED:
  174. if (message.GetSenderId() == m_viewControl.GetCurrentControl())
  175. {
  176. int iItem = m_viewControl.GetSelectedItem();
  177. if (iItem >= 0 && iItem < m_vecItems->Size())
  178. {
  179. const CFileItemPtr item(m_vecItems->Get(iItem));
  180. switch (message.GetParam1())
  181. {
  182. case ACTION_SELECT_ITEM:
  183. case ACTION_MOUSE_LEFT_CLICK:
  184. case ACTION_PLAYER_PLAY:
  185. {
  186. const CPVRRecordingsPath path(m_vecItems->GetPath());
  187. if (path.IsValid() && path.IsRecordingsRoot() && item->IsParentFolder())
  188. {
  189. // handle special 'go home' item.
  190. CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_HOME);
  191. bReturn = true;
  192. break;
  193. }
  194. if (item->m_bIsFolder)
  195. {
  196. // recording folders and ".." folders in subfolders are handled by base class.
  197. bReturn = false;
  198. break;
  199. }
  200. if (message.GetParam1() == ACTION_PLAYER_PLAY)
  201. {
  202. CServiceBroker::GetPVRManager().GUIActions()->PlayRecording(item, true /* check resume */);
  203. bReturn = true;
  204. }
  205. else
  206. {
  207. switch (m_settings.GetIntValue(CSettings::SETTING_MYVIDEOS_SELECTACTION))
  208. {
  209. case SELECT_ACTION_CHOOSE:
  210. OnPopupMenu(iItem);
  211. bReturn = true;
  212. break;
  213. case SELECT_ACTION_PLAY_OR_RESUME:
  214. CServiceBroker::GetPVRManager().GUIActions()->PlayRecording(item, true /* check resume */);
  215. bReturn = true;
  216. break;
  217. case SELECT_ACTION_RESUME:
  218. CServiceBroker::GetPVRManager().GUIActions()->ResumePlayRecording(item, true /* fall back to play if no resume possible */);
  219. bReturn = true;
  220. break;
  221. case SELECT_ACTION_INFO:
  222. CServiceBroker::GetPVRManager().GUIActions()->ShowRecordingInfo(item);
  223. bReturn = true;
  224. break;
  225. default:
  226. bReturn = false;
  227. break;
  228. }
  229. }
  230. break;
  231. }
  232. case ACTION_CONTEXT_MENU:
  233. case ACTION_MOUSE_RIGHT_CLICK:
  234. OnPopupMenu(iItem);
  235. bReturn = true;
  236. break;
  237. case ACTION_SHOW_INFO:
  238. CServiceBroker::GetPVRManager().GUIActions()->ShowRecordingInfo(item);
  239. bReturn = true;
  240. break;
  241. case ACTION_DELETE_ITEM:
  242. CServiceBroker::GetPVRManager().GUIActions()->DeleteRecording(item);
  243. bReturn = true;
  244. break;
  245. default:
  246. bReturn = false;
  247. break;
  248. }
  249. }
  250. }
  251. else if (message.GetSenderId() == CONTROL_BTNGROUPITEMS)
  252. {
  253. const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
  254. settings->ToggleBool(CSettings::SETTING_PVRRECORD_GROUPRECORDINGS);
  255. settings->Save();
  256. Refresh(true);
  257. }
  258. else if (message.GetSenderId() == CONTROL_BTNSHOWDELETED)
  259. {
  260. CGUIRadioButtonControl* radioButton = static_cast<CGUIRadioButtonControl*>(GetControl(CONTROL_BTNSHOWDELETED));
  261. if (radioButton)
  262. {
  263. m_bShowDeletedRecordings = radioButton->IsSelected();
  264. Update(GetDirectoryPath());
  265. }
  266. bReturn = true;
  267. }
  268. else if (message.GetSenderId() == CONTROL_BTNSHOWMODE)
  269. {
  270. CMediaSettings::GetInstance().CycleWatchedMode("recordings");
  271. CServiceBroker::GetSettingsComponent()->GetSettings()->Save();
  272. OnFilterItems(GetProperty("filter").asString());
  273. UpdateButtons();
  274. return true;
  275. }
  276. break;
  277. case GUI_MSG_REFRESH_LIST:
  278. {
  279. switch (static_cast<PVREvent>(message.GetParam1()))
  280. {
  281. case PVREvent::CurrentItem:
  282. case PVREvent::Epg:
  283. case PVREvent::EpgActiveItem:
  284. case PVREvent::EpgContainer:
  285. case PVREvent::Timers:
  286. SetInvalid();
  287. break;
  288. case PVREvent::RecordingsInvalidated:
  289. case PVREvent::TimersInvalidated:
  290. Refresh(true);
  291. break;
  292. default:
  293. break;
  294. }
  295. break;
  296. }
  297. }
  298. return bReturn || CGUIWindowPVRBase::OnMessage(message);
  299. }
  300. bool CGUIWindowPVRRecordingsBase::OnContextButtonDeleteAll(CFileItem* item, CONTEXT_BUTTON button)
  301. {
  302. if (button == CONTEXT_BUTTON_DELETE_ALL)
  303. {
  304. CServiceBroker::GetPVRManager().GUIActions()->DeleteAllRecordingsFromTrash();
  305. return true;
  306. }
  307. return false;
  308. }
  309. void CGUIWindowPVRRecordingsBase::OnPrepareFileItems(CFileItemList& items)
  310. {
  311. if (items.IsEmpty())
  312. return;
  313. CFileItemList files;
  314. for (const auto& item : items)
  315. {
  316. if (!item->m_bIsFolder)
  317. files.Add(item);
  318. }
  319. if (!files.IsEmpty())
  320. {
  321. if (m_database.Open())
  322. {
  323. CGUIWindowVideoNav::LoadVideoInfo(files, m_database, false);
  324. m_database.Close();
  325. }
  326. m_thumbLoader.Load(files);
  327. }
  328. CGUIWindowPVRBase::OnPrepareFileItems(items);
  329. }
  330. bool CGUIWindowPVRRecordingsBase::GetFilteredItems(const std::string& filter, CFileItemList& items)
  331. {
  332. bool listchanged = CGUIWindowPVRBase::GetFilteredItems(filter, items);
  333. int watchMode = CMediaSettings::GetInstance().GetWatchedMode("recordings");
  334. CFileItemPtr item;
  335. for (int i = 0; i < items.Size(); i++)
  336. {
  337. item = items.Get(i);
  338. if (item->IsParentFolder()) // Don't delete the go to parent folder
  339. continue;
  340. if (!item->HasPVRRecordingInfoTag())
  341. continue;
  342. int iPlayCount = item->GetPVRRecordingInfoTag()->GetPlayCount();
  343. if ((watchMode == WatchedModeWatched && iPlayCount == 0) ||
  344. (watchMode == WatchedModeUnwatched && iPlayCount > 0))
  345. {
  346. items.Remove(i);
  347. i--;
  348. listchanged = true;
  349. }
  350. }
  351. // Remove the parent folder item, if it's the only item in the folder.
  352. if (items.GetObjectCount() == 0 && items.GetFileCount() > 0 && items.Get(0)->IsParentFolder())
  353. items.Remove(0);
  354. return listchanged;
  355. }