[pvr] Add deleted recordings trash support
authorAlwinEsch <alwin.esch@web.de>
Sun, 15 Feb 2015 14:20:35 +0000 (15:20 +0100)
committerAlwinEsch <alwin.esch@web.de>
Sun, 15 Feb 2015 15:39:12 +0000 (16:39 +0100)
27 files changed:
xbmc/FileItem.cpp
xbmc/FileItem.h
xbmc/GUIInfoManager.cpp
xbmc/GUIInfoManager.h
xbmc/dialogs/GUIDialogContextMenu.h
xbmc/dialogs/GUIDialogMediaSource.cpp
xbmc/filesystem/PVRDirectory.cpp
xbmc/filesystem/PVRDirectory.h
xbmc/filesystem/PVRFile.cpp
xbmc/pvr/PVRGUIInfo.cpp
xbmc/pvr/PVRGUIInfo.h
xbmc/pvr/addons/PVRClient.cpp
xbmc/pvr/addons/PVRClient.h
xbmc/pvr/addons/PVRClients.cpp
xbmc/pvr/addons/PVRClients.h
xbmc/pvr/recordings/PVRRecording.cpp
xbmc/pvr/recordings/PVRRecording.h
xbmc/pvr/recordings/PVRRecordings.cpp
xbmc/pvr/recordings/PVRRecordings.h
xbmc/pvr/windows/GUIViewStatePVR.cpp
xbmc/pvr/windows/GUIWindowPVRBase.cpp
xbmc/pvr/windows/GUIWindowPVRBase.h
xbmc/pvr/windows/GUIWindowPVRRecordings.cpp
xbmc/pvr/windows/GUIWindowPVRRecordings.h
xbmc/pvr/windows/GUIWindowPVRSearch.cpp
xbmc/video/windows/GUIWindowVideoBase.cpp
xbmc/windows/GUIWindowSystemInfo.cpp

index 755cf46..4515c31 100644 (file)
@@ -739,7 +739,22 @@ bool CFileItem::IsPVRChannel() const
 
 bool CFileItem::IsPVRRecording() const
 {
-  if (HasPVRRecordingInfoTag()) return true; /// is this enough?
+  if (HasPVRRecordingInfoTag())
+    return true;
+  return false;
+}
+
+bool CFileItem::IsUsablePVRRecording() const
+{
+  if (m_pvrRecordingInfoTag && !m_pvrRecordingInfoTag->IsDeleted())
+    return true;
+  return false;
+}
+
+bool CFileItem::IsDeletedPVRRecording() const
+{
+  if (m_pvrRecordingInfoTag && m_pvrRecordingInfoTag->IsDeleted())
+    return true;
   return false;
 }
 
@@ -1166,11 +1181,16 @@ void CFileItem::FillInDefaultIcon()
       { // archive
         SetIconImage("DefaultFile.png");
       }
-      else if ( IsPVRRecording() )
+      else if ( IsUsablePVRRecording() )
       {
         // PVR recording
         SetIconImage("DefaultVideo.png");
       }
+      else if ( IsDeletedPVRRecording() )
+      {
+        // PVR deleted recording
+        SetIconImage("DefaultVideoDeleted.png");
+      }
       else if ( IsAudio() )
       {
         // audio
index e706665..a4c4290 100644 (file)
@@ -204,6 +204,8 @@ public:
   bool IsEPG() const;
   bool IsPVRChannel() const;
   bool IsPVRRecording() const;
+  bool IsUsablePVRRecording() const;
+  bool IsDeletedPVRRecording() const;
   bool IsPVRTimer() const;
   bool IsType(const char *ext) const;
   bool IsVirtualDirectoryRoot() const;
index 79c0186..5b4f8f7 100644 (file)
@@ -657,6 +657,7 @@ const infomap pvr[] =            {{ "isrecording",              PVR_IS_RECORDING
                                   { "backendchannels",          PVR_BACKEND_CHANNELS },
                                   { "backendtimers",            PVR_BACKEND_TIMERS },
                                   { "backendrecordings",        PVR_BACKEND_RECORDINGS },
+                                  { "backenddeletedrecordings", PVR_BACKEND_DELETED_RECORDINGS },
                                   { "backendnumber",            PVR_BACKEND_NUMBER },
                                   { "hasepg",                   PVR_HAS_EPG },
                                   { "hastxt",                   PVR_HAS_TXT },
@@ -1418,6 +1419,7 @@ std::string CGUIInfoManager::GetLabel(int info, int contextWindow, std::string *
   case PVR_BACKEND_CHANNELS:
   case PVR_BACKEND_TIMERS:
   case PVR_BACKEND_RECORDINGS:
+  case PVR_BACKEND_DELETED_RECORDINGS:
   case PVR_BACKEND_NUMBER:
   case PVR_TOTAL_DISKSPACE:
   case PVR_NEXT_TIMER:
index 67e41bd..20fe810 100644 (file)
@@ -466,29 +466,30 @@ namespace INFO
 #define PVR_BACKEND_CHANNELS        (PVR_STRINGS_START + 12)
 #define PVR_BACKEND_TIMERS          (PVR_STRINGS_START + 13)
 #define PVR_BACKEND_RECORDINGS      (PVR_STRINGS_START + 14)
-#define PVR_BACKEND_NUMBER          (PVR_STRINGS_START + 15)
-#define PVR_TOTAL_DISKSPACE         (PVR_STRINGS_START + 16)
-#define PVR_NEXT_TIMER              (PVR_STRINGS_START + 17)
-#define PVR_PLAYING_DURATION        (PVR_STRINGS_START + 18)
-#define PVR_PLAYING_TIME            (PVR_STRINGS_START + 19)
-#define PVR_PLAYING_PROGRESS        (PVR_STRINGS_START + 20)
-#define PVR_ACTUAL_STREAM_CLIENT    (PVR_STRINGS_START + 21)
-#define PVR_ACTUAL_STREAM_DEVICE    (PVR_STRINGS_START + 22)
-#define PVR_ACTUAL_STREAM_STATUS    (PVR_STRINGS_START + 23)
-#define PVR_ACTUAL_STREAM_SIG       (PVR_STRINGS_START + 24)
-#define PVR_ACTUAL_STREAM_SNR       (PVR_STRINGS_START + 25)
-#define PVR_ACTUAL_STREAM_SIG_PROGR (PVR_STRINGS_START + 26)
-#define PVR_ACTUAL_STREAM_SNR_PROGR (PVR_STRINGS_START + 27)
-#define PVR_ACTUAL_STREAM_BER       (PVR_STRINGS_START + 28)
-#define PVR_ACTUAL_STREAM_UNC       (PVR_STRINGS_START + 29)
-#define PVR_ACTUAL_STREAM_VIDEO_BR  (PVR_STRINGS_START + 30)
-#define PVR_ACTUAL_STREAM_AUDIO_BR  (PVR_STRINGS_START + 31)
-#define PVR_ACTUAL_STREAM_DOLBY_BR  (PVR_STRINGS_START + 32)
-#define PVR_ACTUAL_STREAM_CRYPTION  (PVR_STRINGS_START + 33)
-#define PVR_ACTUAL_STREAM_SERVICE   (PVR_STRINGS_START + 34)
-#define PVR_ACTUAL_STREAM_MUX       (PVR_STRINGS_START + 35)
-#define PVR_ACTUAL_STREAM_PROVIDER  (PVR_STRINGS_START + 36)
-#define PVR_BACKEND_DISKSPACE_PROGR (PVR_STRINGS_START + 37)
+#define PVR_BACKEND_DELETED_RECORDINGS (PVR_STRINGS_START + 15)
+#define PVR_BACKEND_NUMBER          (PVR_STRINGS_START + 16)
+#define PVR_TOTAL_DISKSPACE         (PVR_STRINGS_START + 17)
+#define PVR_NEXT_TIMER              (PVR_STRINGS_START + 18)
+#define PVR_PLAYING_DURATION        (PVR_STRINGS_START + 19)
+#define PVR_PLAYING_TIME            (PVR_STRINGS_START + 20)
+#define PVR_PLAYING_PROGRESS        (PVR_STRINGS_START + 21)
+#define PVR_ACTUAL_STREAM_CLIENT    (PVR_STRINGS_START + 22)
+#define PVR_ACTUAL_STREAM_DEVICE    (PVR_STRINGS_START + 23)
+#define PVR_ACTUAL_STREAM_STATUS    (PVR_STRINGS_START + 24)
+#define PVR_ACTUAL_STREAM_SIG       (PVR_STRINGS_START + 25)
+#define PVR_ACTUAL_STREAM_SNR       (PVR_STRINGS_START + 26)
+#define PVR_ACTUAL_STREAM_SIG_PROGR (PVR_STRINGS_START + 27)
+#define PVR_ACTUAL_STREAM_SNR_PROGR (PVR_STRINGS_START + 28)
+#define PVR_ACTUAL_STREAM_BER       (PVR_STRINGS_START + 29)
+#define PVR_ACTUAL_STREAM_UNC       (PVR_STRINGS_START + 30)
+#define PVR_ACTUAL_STREAM_VIDEO_BR  (PVR_STRINGS_START + 31)
+#define PVR_ACTUAL_STREAM_AUDIO_BR  (PVR_STRINGS_START + 32)
+#define PVR_ACTUAL_STREAM_DOLBY_BR  (PVR_STRINGS_START + 33)
+#define PVR_ACTUAL_STREAM_CRYPTION  (PVR_STRINGS_START + 34)
+#define PVR_ACTUAL_STREAM_SERVICE   (PVR_STRINGS_START + 35)
+#define PVR_ACTUAL_STREAM_MUX       (PVR_STRINGS_START + 36)
+#define PVR_ACTUAL_STREAM_PROVIDER  (PVR_STRINGS_START + 37)
+#define PVR_BACKEND_DISKSPACE_PROGR (PVR_STRINGS_START + 38)
 #define PVR_STRINGS_END             PVR_ACTUAL_STREAM_PROVIDER
 
 #define WINDOW_PROPERTY             9993
index 76cf6ca..ce0c2b7 100644 (file)
@@ -129,6 +129,8 @@ enum CONTEXT_BUTTON { CONTEXT_BUTTON_CANCELLED = 0,
                       CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS,
                       CONTEXT_BUTTON_BROWSE_INTO,
                       CONTEXT_BUTTON_EDIT_SORTTITLE,
+                      CONTEXT_BUTTON_UNDELETE,
+                      CONTEXT_BUTTON_DELETE_ALL,
                       CONTEXT_BUTTON_USER1,
                       CONTEXT_BUTTON_USER2,
                       CONTEXT_BUTTON_USER3,
index bce1378..533e6af 100644 (file)
@@ -300,10 +300,16 @@ void CGUIDialogMediaSource::OnPathBrowse(int item)
     // add the recordings dir as needed
     if (CPVRDirectory::HasRecordings())
     {
-      share1.strPath = "pvr://recordings/";
+      share1.strPath = "pvr://recordings/active/";
       share1.strName = g_localizeStrings.Get(19017); // TV Recordings
       extraShares.push_back(share1);
     }
+    if (CPVRDirectory::HasDeletedRecordings())
+    {
+      share1.strPath = "pvr://recordings/deleted/";
+      share1.strName = g_localizeStrings.Get(19108); // Deleted TV Recordings
+      extraShares.push_back(share1);
+    }
   }
   else if (m_type == "pictures")
   {
index 7680f58..e03a9a0 100644 (file)
@@ -71,8 +71,13 @@ bool CPVRDirectory::GetDirectory(const CURL& url, CFileItemList &items)
     item->SetLabelPreformated(true);
     items.Add(item);
 
-    item.reset(new CFileItem(base + "recordings/", true));
-    item->SetLabel(g_localizeStrings.Get(19017));
+    item.reset(new CFileItem(base + "recordings/active/", true));
+    item->SetLabel(g_localizeStrings.Get(19017)); // TV Recordings
+    item->SetLabelPreformated(true);
+    items.Add(item);
+
+    item.reset(new CFileItem(base + "recordings/deleted/", true));
+    item->SetLabel(g_localizeStrings.Get(19108)); // Deleted TV Recordings
     item->SetLabelPreformated(true);
     items.Add(item);
 
@@ -121,3 +126,9 @@ bool CPVRDirectory::HasRecordings()
   return g_PVRManager.IsStarted() ? 
     g_PVRRecordings->GetNumRecordings() > 0 : false;
 }
+
+bool CPVRDirectory::HasDeletedRecordings()
+{
+  return g_PVRManager.IsStarted() ?
+    g_PVRRecordings->HasDeletedRecordings() : false;
+}
index 5df5289..b4443a9 100644 (file)
@@ -38,6 +38,7 @@ public:
   static bool SupportsWriteFileOperations(const std::string& strPath);
   static bool IsLiveTV(const std::string& strPath);
   static bool HasRecordings();
+  static bool HasDeletedRecordings();
 
   virtual bool Exists(const CURL& url);
 
index 0563dd7..2b6a3ac 100644 (file)
@@ -70,7 +70,7 @@ bool CPVRFile::Open(const CURL& url)
       return false;
     }
   }
-  else if (StringUtils::StartsWith(strURL, "pvr://recordings/"))
+  else if (StringUtils::StartsWith(strURL, "pvr://recordings/active"))
   {
     CFileItemPtr tag = g_PVRRecordings->GetByPath(strURL);
     if (tag && tag->HasPVRRecordingInfoTag())
@@ -87,6 +87,11 @@ bool CPVRFile::Open(const CURL& url)
       return false;
     }
   }
+  else if (StringUtils::StartsWith(strURL, "pvr://recordings/deleted/"))
+  {
+    CLog::Log(LOGNOTICE, "PVRFile - Playback of deleted recordings is not possible (%s)", strURL.c_str());
+    return false;
+  }
   else
   {
     CLog::Log(LOGERROR, "%s - invalid path specified %s", __FUNCTION__, strURL.c_str());
@@ -298,7 +303,7 @@ bool CPVRFile::Rename(const CURL& url, const CURL& urlnew)
   if (found != std::string::npos)
     newname = newname.substr(found+1);
 
-  if (StringUtils::StartsWith(path, "recordings/") && path[path.size()-1] != '/')
+  if (StringUtils::StartsWith(path, "recordings/active/") && path[path.size()-1] != '/')
   {
     std::string strURL = url.Get();
     CFileItemPtr tag = g_PVRRecordings->GetByPath(strURL);
index 796a004..843c0b0 100644 (file)
@@ -71,6 +71,7 @@ void CPVRGUIInfo::ResetProperties(void)
   m_strBackendHost              .clear();
   m_strBackendTimers            .clear();
   m_strBackendRecordings        .clear();
+  m_strBackendDeletedRecordings .clear();
   m_strBackendChannels          .clear();
   m_iBackendUsedDiskspace       = 0;
   m_iBackendTotalDiskspace      = 0;
@@ -380,6 +381,9 @@ bool CPVRGUIInfo::TranslateCharInfo(DWORD dwInfo, std::string &strValue) const
   case PVR_BACKEND_RECORDINGS:
     CharInfoBackendRecordings(strValue);
     break;
+  case PVR_BACKEND_DELETED_RECORDINGS:
+    CharInfoBackendDeletedRecordings(strValue);
+    break;
   case PVR_BACKEND_NUMBER:
     CharInfoBackendNumber(strValue);
     break;
@@ -637,6 +641,14 @@ void CPVRGUIInfo::CharInfoBackendRecordings(std::string &strValue) const
     strValue = m_strBackendRecordings;
 }
 
+void CPVRGUIInfo::CharInfoBackendDeletedRecordings(std::string &strValue) const
+{
+  if (m_strBackendDeletedRecordings.empty())
+    strValue = g_localizeStrings.Get(13205); /* Unknown */
+  else
+    strValue = m_strBackendDeletedRecordings;
+}
+
 void CPVRGUIInfo::CharInfoPlayingClientName(std::string &strValue) const
 {
   if (m_strPlayingClientName.empty())
@@ -685,6 +697,7 @@ void CPVRGUIInfo::UpdateBackendCache(void)
   std::string strBackendHost;
   std::string strBackendTimers;
   std::string strBackendRecordings;
+  std::string strBackendDeletedRecordings;
   std::string strBackendChannels;
   long long   iBackendkBUsed(0);
   long long   iBackendkBTotal(0);
@@ -727,27 +740,34 @@ void CPVRGUIInfo::UpdateBackendCache(void)
     else
       strBackendTimers = g_localizeStrings.Get(161);
 
-    int NumRecordings = activeClient->second->GetRecordingsAmount();
+    int NumRecordings = activeClient->second->GetRecordingsAmount(false);
     if (NumRecordings >= 0)
       strBackendRecordings = StringUtils::Format("%i", NumRecordings);
     else
       strBackendRecordings = g_localizeStrings.Get(161);
 
+    int NumDeletedRecordings = activeClient->second->GetRecordingsAmount(true);
+    if (NumDeletedRecordings >= 0)
+      strBackendDeletedRecordings = StringUtils::Format("%i", NumDeletedRecordings);
+    else
+      strBackendDeletedRecordings = g_localizeStrings.Get(161); /* Unavailable */
+
     strBackendName    = activeClient->second->GetBackendName();
     strBackendVersion = activeClient->second->GetBackendVersion();
     strBackendHost    = activeClient->second->GetConnectionString();
   }
 
   CSingleLock lock(m_critSection);
-  m_strBackendName         = strBackendName;
-  m_strBackendVersion      = strBackendVersion;
-  m_strBackendHost         = strBackendHost;
-  m_strBackendTimers       = strBackendTimers;
-  m_strBackendRecordings   = strBackendRecordings;
-  m_strBackendChannels     = strBackendChannels;
-  m_iActiveClients         = iActiveClients;
-  m_iBackendUsedDiskspace  = iBackendkBUsed;
-  m_iBackendTotalDiskspace = iBackendkBTotal;
+  m_strBackendName              = strBackendName;
+  m_strBackendVersion           = strBackendVersion;
+  m_strBackendHost              = strBackendHost;
+  m_strBackendTimers            = strBackendTimers;
+  m_strBackendRecordings        = strBackendRecordings;
+  m_strBackendDeletedRecordings = strBackendDeletedRecordings;
+  m_strBackendChannels          = strBackendChannels;
+  m_iActiveClients              = iActiveClients;
+  m_iBackendUsedDiskspace       = iBackendkBUsed;
+  m_iBackendTotalDiskspace      = iBackendkBTotal;
 }
 
 void CPVRGUIInfo::UpdateTimersCache(void)
index e71cc9d..d285345 100644 (file)
@@ -134,6 +134,7 @@ namespace PVR
     void CharInfoBackendChannels(std::string &strValue) const;
     void CharInfoBackendTimers(std::string &strValue) const;
     void CharInfoBackendRecordings(std::string &strValue) const;
+    void CharInfoBackendDeletedRecordings(std::string &strValue) const;
     void CharInfoPlayingClientName(std::string &strValue) const;
     void CharInfoEncryption(std::string &strValue) const;
     void CharInfoService(std::string &strValue) const;
@@ -161,6 +162,7 @@ namespace PVR
     std::string                     m_strBackendHost;
     std::string                     m_strBackendTimers;
     std::string                     m_strBackendRecordings;
+    std::string                     m_strBackendDeletedRecordings;
     std::string                     m_strBackendChannels;
     long long                       m_iBackendUsedDiskspace;
     long long                       m_iBackendTotalDiskspace;
index 596b3ab..2477326 100644 (file)
@@ -246,6 +246,7 @@ void CPVRClient::WriteClientRecordingInfo(const CPVRRecording &xbmcRecording, PV
   addonRecording.iLifetime           = xbmcRecording.m_iLifetime;
   addonRecording.iPlayCount          = xbmcRecording.m_playCount;
   addonRecording.iLastPlayedPosition = (int)xbmcRecording.m_resumePoint.timeInSeconds;
+  addonRecording.bIsDeleted          = xbmcRecording.IsDeleted();
   strncpy(addonRecording.strDirectory, xbmcRecording.m_strDirectory.c_str(), sizeof(addonRecording.strDirectory) - 1);
   strncpy(addonRecording.strStreamURL, xbmcRecording.m_strStreamURL.c_str(), sizeof(addonRecording.strStreamURL) - 1);
   strncpy(addonRecording.strIconPath, xbmcRecording.m_strIconPath.c_str(), sizeof(addonRecording.strIconPath) - 1);
@@ -270,6 +271,7 @@ void CPVRClient::WriteClientTimerInfo(const CPVRTimerInfoTag &xbmcTimer, PVR_TIM
 
   addonTimer.iClientIndex      = xbmcTimer.m_iClientIndex;
   addonTimer.state             = xbmcTimer.m_state;
+  addonTimer.iClientIndex      = xbmcTimer.m_iClientIndex;
   addonTimer.iClientChannelUid = xbmcTimer.m_iClientChannelUid;
   strncpy(addonTimer.strTitle, xbmcTimer.m_strTitle.c_str(), sizeof(addonTimer.strTitle) - 1);
   strncpy(addonTimer.strDirectory, xbmcTimer.m_strDirectory.c_str(), sizeof(addonTimer.strDirectory) - 1);
@@ -480,11 +482,16 @@ void CPVRClient::CallMenuHook(const PVR_MENUHOOK &hook, const CFileItem *item)
         hookData.cat = PVR_MENUHOOK_CHANNEL;
         WriteClientChannelInfo(*item->GetPVRChannelInfoTag(), hookData.data.channel);
       }
-      else if (item->IsPVRRecording())
+      else if (item->IsUsablePVRRecording())
       {
         hookData.cat = PVR_MENUHOOK_RECORDING;
         WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording);
       }
+      else if (item->IsDeletedPVRRecording())
+      {
+        hookData.cat = PVR_MENUHOOK_DELETED_RECORDING;
+        WriteClientRecordingInfo(*item->GetPVRRecordingInfoTag(), hookData.data.recording);
+      }
       else if (item->IsPVRTimer())
       {
         hookData.cat = PVR_MENUHOOK_TIMER;
@@ -644,25 +651,31 @@ PVR_ERROR CPVRClient::GetChannels(CPVRChannelGroup &channels, bool radio)
   return retVal;
 }
 
-int CPVRClient::GetRecordingsAmount(void)
+int CPVRClient::GetRecordingsAmount(bool deleted)
 {
   int iReturn(-EINVAL);
 
-  if (m_addonCapabilities.bSupportsRecordings)
+  if (!m_addonCapabilities.bSupportsRecordings || (deleted && !m_addonCapabilities.bSupportsRecordingsUndelete))
+    return iReturn;
+
+  try
   {
-    try { iReturn = m_pStruct->GetRecordingsAmount(); }
-    catch (std::exception &e) { LogException(e, __FUNCTION__); }
+    iReturn = m_pStruct->GetRecordingsAmount(deleted);
+  }
+  catch (std::exception &e)
+  {
+    LogException(e, __FUNCTION__);
   }
 
   return iReturn;
 }
 
-PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings *results)
+PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings *results, bool deleted)
 {
   if (!m_bReadyToUse)
     return PVR_ERROR_REJECTED;
 
-  if (!m_addonCapabilities.bSupportsRecordings)
+  if (!m_addonCapabilities.bSupportsRecordings || (deleted && !m_addonCapabilities.bSupportsRecordingsUndelete))
     return PVR_ERROR_NOT_IMPLEMENTED;
 
   PVR_ERROR retVal(PVR_ERROR_UNKNOWN);
@@ -671,7 +684,7 @@ PVR_ERROR CPVRClient::GetRecordings(CPVRRecordings *results)
     ADDON_HANDLE_STRUCT handle;
     handle.callerAddress = this;
     handle.dataAddress = (CPVRRecordings*) results;
-    retVal = m_pStruct->GetRecordings(&handle);
+    retVal = m_pStruct->GetRecordings(&handle, deleted);
 
     LogError(retVal, __FUNCTION__);
   }
@@ -709,6 +722,55 @@ PVR_ERROR CPVRClient::DeleteRecording(const CPVRRecording &recording)
   return retVal;
 }
 
+PVR_ERROR CPVRClient::UndeleteRecording(const CPVRRecording &recording)
+{
+  if (!m_bReadyToUse)
+    return PVR_ERROR_REJECTED;
+
+  if (!m_addonCapabilities.bSupportsRecordingsUndelete)
+    return PVR_ERROR_NOT_IMPLEMENTED;
+
+  PVR_ERROR retVal(PVR_ERROR_UNKNOWN);
+  try
+  {
+    PVR_RECORDING tag;
+    WriteClientRecordingInfo(recording, tag);
+
+    retVal = m_pStruct->UndeleteRecording(tag);
+
+    LogError(retVal, __FUNCTION__);
+  }
+  catch (std::exception &e)
+  {
+    LogException(e, __FUNCTION__);
+  }
+
+  return retVal;
+}
+
+PVR_ERROR CPVRClient::DeleteAllRecordingsFromTrash()
+{
+  if (!m_bReadyToUse)
+    return PVR_ERROR_REJECTED;
+
+  if (!m_addonCapabilities.bSupportsRecordingsUndelete)
+    return PVR_ERROR_NOT_IMPLEMENTED;
+
+  PVR_ERROR retVal(PVR_ERROR_UNKNOWN);
+  try
+  {
+    retVal = m_pStruct->DeleteAllRecordingsFromTrash();
+
+    LogError(retVal, __FUNCTION__);
+  }
+  catch (std::exception &e)
+  {
+    LogException(e, __FUNCTION__);
+  }
+
+  return retVal;
+}
+
 PVR_ERROR CPVRClient::RenameRecording(const CPVRRecording &recording)
 {
   if (!m_bReadyToUse)
@@ -1283,6 +1345,11 @@ bool CPVRClient::SupportsRecordings(void) const
   return m_addonCapabilities.bSupportsRecordings;
 }
 
+bool CPVRClient::SupportsRecordingsUndelete(void) const
+{
+  return m_addonCapabilities.bSupportsRecordingsUndelete;
+}
+
 bool CPVRClient::SupportsRecordingFolders(void) const
 {
   return m_addonCapabilities.bSupportsRecordingFolders;
index b6f3eb5..dd31760 100644 (file)
@@ -233,16 +233,18 @@ namespace PVR
     //@{
 
     /*!
-     * @return The total amount of channels on the server or -1 on error.
+     * @param deleted if set return deleted recording
+     * @return The total amount of recordingd on the server or -1 on error.
      */
-    int GetRecordingsAmount(void);
+    int GetRecordingsAmount(bool deleted);
 
     /*!
      * @brief Request the list of all recordings from the backend.
      * @param results The container to add the recordings to.
+     * @param deleted if set return deleted recording
      * @return PVR_ERROR_NO_ERROR if the list has been fetched successfully.
      */
-    PVR_ERROR GetRecordings(CPVRRecordings *results);
+    PVR_ERROR GetRecordings(CPVRRecordings *results, bool deleted);
 
     /*!
      * @brief Delete a recording on the backend.
@@ -252,6 +254,19 @@ namespace PVR
     PVR_ERROR DeleteRecording(const CPVRRecording &recording);
 
     /*!
+     * @brief Undelete a recording on the backend.
+     * @param recording The recording to undelete.
+     * @return PVR_ERROR_NO_ERROR if the recording has been undeleted successfully.
+     */
+    PVR_ERROR UndeleteRecording(const CPVRRecording &recording);
+
+    /*!
+     * @brief Delete all recordings permanent which in the deleted folder on the backend.
+     * @return PVR_ERROR_NO_ERROR if the recordings has been deleted successfully.
+     */
+    PVR_ERROR DeleteAllRecordingsFromTrash();
+
+    /*!
      * @brief Rename a recording on the backend.
      * @param recording The recording to rename.
      * @return PVR_ERROR_NO_ERROR if the recording has been renamed successfully.
@@ -479,6 +494,7 @@ namespace PVR
     bool SupportsLastPlayedPosition(void) const;
     bool SupportsRadio(void) const;
     bool SupportsRecordings(void) const;
+    bool SupportsRecordingsUndelete(void) const;
     bool SupportsRecordingFolders(void) const;
     bool SupportsRecordingPlayCount(void) const;
     bool SupportsRecordingEdl(void) const;
index 961373b..d6ede71 100644 (file)
@@ -438,7 +438,7 @@ PVR_ERROR CPVRClients::RenameTimer(const CPVRTimerInfoTag &timer, const std::str
   return error;
 }
 
-PVR_ERROR CPVRClients::GetRecordings(CPVRRecordings *recordings)
+PVR_ERROR CPVRClients::GetRecordings(CPVRRecordings *recordings, bool deleted)
 {
   PVR_ERROR error(PVR_ERROR_NO_ERROR);
   PVR_CLIENTMAP clients;
@@ -446,7 +446,7 @@ PVR_ERROR CPVRClients::GetRecordings(CPVRRecordings *recordings)
 
   for (PVR_CLIENTMAP_CITR itrClients = clients.begin(); itrClients != clients.end(); itrClients++)
   {
-    PVR_ERROR currentError = (*itrClients).second->GetRecordings(recordings);
+    PVR_ERROR currentError = (*itrClients).second->GetRecordings(recordings, deleted);
     if (currentError != PVR_ERROR_NOT_IMPLEMENTED &&
         currentError != PVR_ERROR_NO_ERROR)
     {
@@ -486,6 +486,81 @@ PVR_ERROR CPVRClients::DeleteRecording(const CPVRRecording &recording)
   return error;
 }
 
+PVR_ERROR CPVRClients::UndeleteRecording(const CPVRRecording &recording)
+{
+  PVR_ERROR error(PVR_ERROR_UNKNOWN);
+
+  if (!recording.IsDeleted())
+    return error;
+
+  PVR_CLIENT client;
+  if (GetConnectedClient(recording.m_iClientId, client))
+    error = client->UndeleteRecording(recording);
+
+  if (error != PVR_ERROR_NO_ERROR)
+    CLog::Log(LOGERROR, "PVR - %s - cannot undelete recording from client '%d': %s",__FUNCTION__, recording.m_iClientId, CPVRClient::ToString(error));
+
+  return error;
+}
+
+PVR_ERROR CPVRClients::DeleteAllRecordingsFromTrash()
+{
+  PVR_ERROR error(PVR_ERROR_NO_ERROR);
+  PVR_CLIENTMAP clients;
+  GetConnectedClients(clients);
+
+  std::vector<PVR_CLIENT> suppClients;
+  for (PVR_CLIENTMAP_CITR itrClients = clients.begin(); itrClients != clients.end(); ++itrClients)
+  {
+    if (itrClients->second->SupportsRecordingsUndelete() && itrClients->second->GetRecordingsAmount(true) > 0)
+      suppClients.push_back(itrClients->second);
+  }
+
+  int selection = 0;
+  if (suppClients.size() > 1)
+  {
+    // have user select client
+    CGUIDialogSelect* pDialog = (CGUIDialogSelect*)g_windowManager.GetWindow(WINDOW_DIALOG_SELECT);
+    pDialog->Reset();
+    pDialog->SetHeading(19292);                 /* Delete all permanently */
+    pDialog->Add(g_localizeStrings.Get(24032)); /* All Add-ons */
+
+    PVR_CLIENTMAP_CITR itrClients;
+    for (itrClients = clients.begin(); itrClients != clients.end(); ++itrClients)
+    {
+      if (itrClients->second->SupportsRecordingsUndelete() && itrClients->second->GetRecordingsAmount(true) > 0)
+        pDialog->Add(itrClients->second->GetBackendName());
+    }
+    pDialog->DoModal();
+    selection = pDialog->GetSelectedLabel();
+  }
+
+  if (selection == 0)
+  {
+    typedef std::vector<PVR_CLIENT>::const_iterator suppClientsCITR;
+    for (suppClientsCITR itrSuppClients = suppClients.begin(); itrSuppClients != suppClients.end(); ++itrSuppClients)
+    {
+      PVR_ERROR currentError = (*itrSuppClients)->DeleteAllRecordingsFromTrash();
+      if (currentError != PVR_ERROR_NO_ERROR)
+      {
+        CLog::Log(LOGERROR, "PVR - %s - cannot delete all recordings from client '%d': %s",__FUNCTION__, (*itrSuppClients)->GetID(), CPVRClient::ToString(currentError));
+        error = currentError;
+      }
+    }
+  }
+  else if (selection >= 1 && selection <= (int)suppClients.size())
+  {
+    PVR_ERROR currentError = suppClients[selection-1]->DeleteAllRecordingsFromTrash();
+    if (currentError != PVR_ERROR_NO_ERROR)
+    {
+      CLog::Log(LOGERROR, "PVR - %s - cannot delete all recordings from client '%d': %s",__FUNCTION__, suppClients[selection-1]->GetID(), CPVRClient::ToString(currentError));
+      error = currentError;
+    }
+  }
+
+  return error;
+}
+
 bool CPVRClients::SetRecordingLastPlayedPosition(const CPVRRecording &recording, int lastplayedposition, PVR_ERROR *error)
 {
   *error = PVR_ERROR_UNKNOWN;
@@ -1120,6 +1195,12 @@ bool CPVRClients::SupportsRecordings(int iClientId) const
   return GetConnectedClient(iClientId, client) && client->SupportsRecordings();
 }
 
+bool CPVRClients::SupportsRecordingsUndelete(int iClientId) const
+{
+  PVR_CLIENT client;
+  return GetConnectedClient(iClientId, client) && client->SupportsRecordingsUndelete();
+}
+
 bool CPVRClients::SupportsRecordingFolders(int iClientId) const
 {
   PVR_CLIENT client;
index 23bb080..6652c21 100644 (file)
@@ -362,11 +362,19 @@ namespace PVR
     bool SupportsRecordings(int iClientId) const;
 
     /*!
+     * @brief Check whether a client supports undelete of recordings.
+     * @param iClientId The id of the client to check.
+     * @return True if the supports undeleted of recordings, false otherwise.
+     */
+    bool SupportsRecordingsUndelete(int iClientId) const;
+
+    /*!
      * @brief Get all recordings from clients
      * @param recordings Store the recordings in this container.
+     * @param deleted Return deleted recordings
      * @return The amount of recordings that were added.
      */
-    PVR_ERROR GetRecordings(CPVRRecordings *recordings);
+    PVR_ERROR GetRecordings(CPVRRecordings *recordings, bool deleted);
 
     /*!
      * @brief Rename a recordings on the backend.
@@ -385,6 +393,20 @@ namespace PVR
     PVR_ERROR DeleteRecording(const CPVRRecording &recording);
 
     /*!
+     * @brief Undelete a recording from the backend.
+     * @param recording The recording to undelete.
+     * @param error An error if it occured.
+     * @return True if the recording was undeleted successfully, false otherwise.
+     */
+    PVR_ERROR UndeleteRecording(const CPVRRecording &recording);
+
+    /*!
+     * @brief Delete all recordings permanent which in the deleted folder on the backend.
+     * @return PVR_ERROR_NO_ERROR if the recordings has been deleted successfully.
+     */
+    PVR_ERROR DeleteAllRecordingsFromTrash();
+
+    /*!
      * @brief Set play count of a recording on the backend.
      * @param recording The recording to set the play count.
      * @param count Play count.
index bb1713c..96153a0 100644 (file)
@@ -103,6 +103,7 @@ CPVRRecording::CPVRRecording(const PVR_RECORDING &recording, unsigned int iClien
   m_strIconPath                    = recording.strIconPath;
   m_strThumbnailPath               = recording.strThumbnailPath;
   m_strFanartPath                  = recording.strFanartPath;
+  m_bIsDeleted                     = recording.bIsDeleted;
 }
 
 bool CPVRRecording::operator ==(const CPVRRecording& right) const
@@ -124,7 +125,8 @@ bool CPVRRecording::operator ==(const CPVRRecording& right) const
        m_strIconPath        == right.m_strIconPath &&
        m_strThumbnailPath   == right.m_strThumbnailPath &&
        m_strFanartPath      == right.m_strFanartPath &&
-       m_iRecordingId       == right.m_iRecordingId);
+       m_iRecordingId       == right.m_iRecordingId &&
+       m_bIsDeleted         == right.m_bIsDeleted);
 }
 
 bool CPVRRecording::operator !=(const CPVRRecording& right) const
@@ -145,6 +147,7 @@ void CPVRRecording::Serialize(CVariant& value) const
   value["starttime"] = m_recordingTime.IsValid() ? m_recordingTime.GetAsDBDateTime() : "";
   value["endtime"] = m_recordingTime.IsValid() ? (m_recordingTime + m_duration).GetAsDBDateTime() : "";
   value["recordingid"] = m_iRecordingId;
+  value["deleted"] = m_bIsDeleted;
 
   if (!value.isMember("art"))
     value["art"] = CVariant(CVariant::VariantTypeObject);
@@ -169,6 +172,7 @@ void CPVRRecording::Reset(void)
   m_strFanartPath      .clear();
   m_bGotMetaData       = false;
   m_iRecordingId       = 0;
+  m_bIsDeleted         = false;
 
   m_recordingTime.Reset();
   CVideoInfoTag::Reset();
@@ -194,6 +198,18 @@ bool CPVRRecording::Delete(void)
   return true;
 }
 
+bool CPVRRecording::Undelete(void)
+{
+  PVR_ERROR error = g_PVRClients->UndeleteRecording(*this);
+  if (error != PVR_ERROR_NO_ERROR)
+  {
+    DisplayError(error);
+    return false;
+  }
+
+  return true;
+}
+
 bool CPVRRecording::Rename(const std::string &strNewName)
 {
   m_strTitle = StringUtils::Format("%s", strNewName.c_str());
@@ -315,6 +331,7 @@ void CPVRRecording::Update(const CPVRRecording &tag)
   m_strIconPath       = tag.m_strIconPath;
   m_strThumbnailPath  = tag.m_strThumbnailPath;
   m_strFanartPath     = tag.m_strFanartPath;
+  m_bIsDeleted        = tag.m_bIsDeleted;
 
   if (g_PVRClients->SupportsRecordingPlayCount(m_iClientId))
     m_playCount       = tag.m_playCount;
@@ -360,7 +377,7 @@ void CPVRRecording::UpdatePath(void)
       strDirectory = StringUtils::Format("%s/", m_strDirectory.c_str());
     if (!m_strChannelName.empty())
       strChannel = StringUtils::Format(" (%s)", m_strChannelName.c_str());
-    m_strFileNameAndPath = StringUtils::Format("pvr://recordings/%s%s, TV%s, %s.pvr", strDirectory.c_str(), strTitle.c_str(), strChannel.c_str(), strDatetime.c_str());
+    m_strFileNameAndPath = StringUtils::Format("pvr://recordings/%s/%s%s, TV%s, %s.pvr", (m_bIsDeleted ? "deleted" : "active"),  strDirectory.c_str(), strTitle.c_str(), strChannel.c_str(), strDatetime.c_str());
   }
 }
 
index c3a9ada..0eb0abb 100644 (file)
@@ -115,6 +115,12 @@ namespace PVR
     bool Delete(void);
 
     /*!
+     * @brief Undelete this recording on the client (if supported).
+     * @return True if it was undeleted successfully, false otherwise.
+     */
+    bool Undelete(void);
+
+    /*!
      * @brief Rename this recording on the client (if supported).
      * @param strNewName The new name.
      * @return True if it was renamed successfully, false otherwise.
@@ -183,9 +189,15 @@ namespace PVR
      */
     void CopyClientInfo(CVideoInfoTag *target) const;
 
+    /*!
+     * @brief If deleted but can be undeleted it is true
+     */
+    bool IsDeleted() const { return m_bIsDeleted; }
+
   private:
     CDateTime m_recordingTime; /*!< start time of the recording */
     bool      m_bGotMetaData;
+    bool      m_bIsDeleted;    /*!< set if entry is a deleted recording which can be undelete */
 
     void UpdatePath(void);
     void DisplayError(PVR_ERROR err) const;
index 56eff34..91a7880 100644 (file)
@@ -57,7 +57,8 @@ void CPVRRecordings::UpdateFromClients(void)
 {
   CSingleLock lock(m_critSection);
   Clear();
-  g_PVRClients->GetRecordings(this);
+  g_PVRClients->GetRecordings(this, false);
+  g_PVRClients->GetRecordings(this, true);
 }
 
 std::string CPVRRecordings::TrimSlashes(const std::string &strOrig) const
@@ -107,10 +108,11 @@ bool CPVRRecordings::IsDirectoryMember(const std::string &strDirectory, const st
         StringUtils::StartsWithNoCase(strUseEntryDirectory, strUseDirectory);
 }
 
-void CPVRRecordings::GetSubDirectories(const std::string &strBase, CFileItemList *results)
+void CPVRRecordings::GetSubDirectories(const std::string &strBase, CFileItemList *results, bool bDeleted)
 {
   std::string strUseBase = TrimSlashes(strBase);
   std::set<CFileItemPtr> unwatchedFolders;
+  std::string strBasePath = bDeleted ? "deleted" : "active";
 
   for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++)
   {
@@ -121,9 +123,9 @@ void CPVRRecordings::GetSubDirectories(const std::string &strBase, CFileItemList
 
     std::string strFilePath;
     if(strUseBase.empty())
-      strFilePath = StringUtils::Format("pvr://recordings/%s/", strCurrent.c_str());
+      strFilePath = StringUtils::Format("pvr://recordings/%s/%s/", strBasePath.c_str(), strCurrent.c_str());
     else
-      strFilePath = StringUtils::Format("pvr://recordings/%s/%s/", strUseBase.c_str(), strCurrent.c_str());
+      strFilePath = StringUtils::Format("pvr://recordings/%s/%s/%s/", strBasePath.c_str(), strUseBase.c_str(), strCurrent.c_str());
 
     CFileItemPtr pFileItem;
     if (m_database.IsOpen())
@@ -194,17 +196,28 @@ int CPVRRecordings::GetNumRecordings()
   return m_recordings.size();
 }
 
-int CPVRRecordings::GetRecordings(CFileItemList* results)
+bool CPVRRecordings::HasDeletedRecordings()
+{
+  CSingleLock lock(m_critSection);
+  return m_bHasDeleted;
+}
+
+int CPVRRecordings::GetRecordings(CFileItemList* results, bool bDeleted)
 {
   CSingleLock lock(m_critSection);
 
+  int iRecCount = 0;
   for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++)
   {
+    if (it->second->IsDeleted() != bDeleted)
+      continue;
+
     CFileItemPtr pFileItem(new CFileItem(it->second));
     results->Add(pFileItem);
+    iRecCount++;
   }
 
-  return m_recordings.size();
+  return iRecCount;
 }
 
 bool CPVRRecordings::Delete(const CFileItem& item)
@@ -240,9 +253,21 @@ bool CPVRRecordings::DeleteRecording(const CFileItem &item)
   return tag->Delete();
 }
 
+bool CPVRRecordings::Undelete(const CFileItem &item)
+{
+  if (!item.IsDeletedPVRRecording())
+  {
+    CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot undelete file: no valid recording tag", __FUNCTION__);
+    return false;
+  }
+
+  CPVRRecordingPtr tag = item.GetPVRRecordingInfoTag();
+  return tag->Undelete();
+}
+
 bool CPVRRecordings::RenameRecording(CFileItem &item, std::string &strNewName)
 {
-  if (!item.IsPVRRecording())
+  if (!item.IsUsablePVRRecording())
   {
     CLog::Log(LOGERROR, "CPVRRecordings - %s - cannot rename file: no valid recording tag", __FUNCTION__);
     return false;
@@ -252,6 +277,11 @@ bool CPVRRecordings::RenameRecording(CFileItem &item, std::string &strNewName)
   return tag->Rename(strNewName);
 }
 
+bool CPVRRecordings::DeleteAllRecordingsFromTrash()
+{
+  return g_PVRClients->DeleteAllRecordingsFromTrash();
+}
+
 bool CPVRRecordings::SetRecordingsPlayCount(const CFileItemPtr &item, int count)
 {
   bool bResult = false;
@@ -320,9 +350,13 @@ bool CPVRRecordings::GetDirectory(const std::string& strPath, CFileItemList &ite
   {
     strDirectoryPath.erase(0, 10);
 
+    // Check directory name is for deleted recordings
+    bool bDeleted = StringUtils::StartsWith(strDirectoryPath, "/deleted");
+    strDirectoryPath.erase(0, bDeleted ? 8 : 7);
+
     // get the directory structure if in non-flatten mode
     if (m_bGroupItems)
-      GetSubDirectories(strDirectoryPath, &items);
+      GetSubDirectories(strDirectoryPath, &items, bDeleted);
 
     // get all files of the currrent directory or recursively all files starting at the current directory if in flatten mode
     for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++)
@@ -330,7 +364,7 @@ bool CPVRRecordings::GetDirectory(const std::string& strPath, CFileItemList &ite
       CPVRRecordingPtr current = it->second;
 
       // skip items that are not members of the target directory
-      if (!IsDirectoryMember(strDirectoryPath, current->m_strDirectory))
+      if (!IsDirectoryMember(strDirectoryPath, current->m_strDirectory) || current->IsDeleted() != bDeleted)
         continue;
 
       if (m_database.IsOpen())
@@ -367,12 +401,15 @@ bool CPVRRecordings::GetDirectory(const std::string& strPath, CFileItemList &ite
   return false;
 }
 
-void CPVRRecordings::GetAll(CFileItemList &items)
+void CPVRRecordings::GetAll(CFileItemList &items, bool bDeleted)
 {
   CSingleLock lock(m_critSection);
   for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++)
   {
     CPVRRecordingPtr current = it->second;
+    if (current->IsDeleted() != bDeleted)
+      continue;
+
     if (m_database.IsOpen())
       current->UpdateMetadata(m_database);
 
@@ -409,14 +446,18 @@ CFileItemPtr CPVRRecordings::GetByPath(const std::string &path)
 
   if (StringUtils::StartsWith(fileName, "recordings/"))
   {
+    // Check directory name is for deleted recordings
+    fileName.erase(0, 11);
+    bool bDeleted = StringUtils::StartsWith(fileName, "deleted/");
+
     for (PVR_RECORDINGMAP_CITR it = m_recordings.begin(); it != m_recordings.end(); it++)
     {
       CPVRRecordingPtr current = it->second;
-      if (URIUtils::PathEquals(path, current->m_strFileNameAndPath))
-      {
-        CFileItemPtr fileItem(new CFileItem(current));
-        return fileItem;
-      }
+      if (!URIUtils::PathEquals(path, current->m_strFileNameAndPath) || bDeleted != current->IsDeleted())
+        continue;
+
+      CFileItemPtr fileItem(new CFileItem(current));
+      return fileItem;
     }
   }
 
@@ -438,6 +479,7 @@ CPVRRecordingPtr CPVRRecordings::GetById(int iClientId, const std::string &strRe
 void CPVRRecordings::Clear()
 {
   CSingleLock lock(m_critSection);
+  m_bHasDeleted = false;
   m_recordings.clear();
 }
 
@@ -445,6 +487,9 @@ void CPVRRecordings::UpdateFromClient(const CPVRRecordingPtr &tag)
 {
   CSingleLock lock(m_critSection);
 
+  if (tag->IsDeleted())
+    m_bHasDeleted = true;
+
   CPVRRecordingPtr newTag = GetById(tag->m_iClientId, tag->m_strRecordingId);
   if (newTag)
   {
index 9463887..367c01a 100644 (file)
@@ -42,12 +42,13 @@ namespace PVR
     unsigned int                 m_iLastId;
     bool                         m_bGroupItems;
     CVideoDatabase               m_database;
+    bool                         m_bHasDeleted;
 
     virtual void UpdateFromClients(void);
     virtual std::string TrimSlashes(const std::string &strOrig) const;
     virtual const std::string GetDirectoryFromPath(const std::string &strPath, const std::string &strBase) const;
     virtual bool IsDirectoryMember(const std::string &strDirectory, const std::string &strEntryDirectory) const;
-    virtual void GetSubDirectories(const std::string &strBase, CFileItemList *results);
+    virtual void GetSubDirectories(const std::string &strBase, CFileItemList *results, bool bDeleted = false);
 
     /**
      * @brief recursively deletes all recordings in the specified directory
@@ -72,7 +73,8 @@ namespace PVR
     void Update(void);
 
     int GetNumRecordings();
-    int GetRecordings(CFileItemList* results);
+    bool HasDeletedRecordings();
+    int GetRecordings(CFileItemList* results, bool bDeleted = false);
     
     /**
      * Deletes the item in question, be it a directory or a file
@@ -80,13 +82,15 @@ namespace PVR
      * @return whether the item was deleted successfully
      */
     bool Delete(const CFileItem &item);
+    bool Undelete(const CFileItem &item);
+    bool DeleteAllRecordingsFromTrash();
     bool RenameRecording(CFileItem &item, std::string &strNewName);
     bool SetRecordingsPlayCount(const CFileItemPtr &item, int count);
 
     bool GetDirectory(const std::string& strPath, CFileItemList &items);
     CFileItemPtr GetByPath(const std::string &path);
     CPVRRecordingPtr GetById(int iClientId, const std::string &strRecordingId) const;
-    void GetAll(CFileItemList &items);
+    void GetAll(CFileItemList &items, bool bDeleted = false);
     CFileItemPtr GetById(unsigned int iId) const;
 
     void SetGroupItems(bool value) { m_bGroupItems = value; };
index 3941bfa..7f33580 100644 (file)
@@ -63,7 +63,7 @@ void CGUIViewStateWindowPVRRecordings::SaveViewState(void)
 
 bool CGUIViewStateWindowPVRRecordings::HideParentDirItems(void)
 {
-  return (CGUIViewState::HideParentDirItems() || m_items.GetPath() == "pvr://recordings/");
+  return (CGUIViewState::HideParentDirItems() || m_items.GetPath() == "pvr://recordings/active/" || m_items.GetPath() == "pvr://recordings/deleted/");
 }
 
 CGUIViewStateWindowPVRGuide::CGUIViewStateWindowPVRGuide(const int windowId, const CFileItemList& items) : CGUIViewStatePVR(windowId, items)
index 025125c..f1dfde7 100644 (file)
@@ -178,7 +178,9 @@ bool CGUIWindowPVRBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
           g_PVRClients->ProcessMenuHooks(item->GetEPGInfoTag()->ChannelTag()->ClientID(), PVR_MENUHOOK_EPG, item.get());
         else if (item->IsPVRChannel())
           g_PVRClients->ProcessMenuHooks(item->GetPVRChannelInfoTag()->ClientID(), PVR_MENUHOOK_CHANNEL, item.get());
-        else if (item->IsPVRRecording())
+        else if (item->IsDeletedPVRRecording())
+          g_PVRClients->ProcessMenuHooks(item->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_DELETED_RECORDING, item.get());
+        else if (item->IsUsablePVRRecording())
           g_PVRClients->ProcessMenuHooks(item->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_RECORDING, item.get());
         else if (item->IsPVRTimer())
           g_PVRClients->ProcessMenuHooks(item->GetPVRTimerInfoTag()->m_iClientId, PVR_MENUHOOK_TIMER, item.get());
index 6bfbe38..f8f0b03 100644 (file)
@@ -28,6 +28,7 @@
 #define CONTROL_BTNSORTASC                4
 #define CONTROL_BTNGROUPITEMS             5
 #define CONTROL_BTNSHOWHIDDEN             6
+#define CONTROL_BTNSHOWDELETED            7
 #define CONTROL_BTNCHANNELGROUPS          28
 #define CONTROL_BTNFILTERCHANNELS         31
 
index 8f20bad..7ebafc1 100644 (file)
@@ -39,7 +39,8 @@
 using namespace PVR;
 
 CGUIWindowPVRRecordings::CGUIWindowPVRRecordings(bool bRadio) :
-  CGUIWindowPVRBase(bRadio, bRadio ? WINDOW_RADIO_RECORDINGS : WINDOW_TV_RECORDINGS, "MyPVRRecordings.xml")
+  CGUIWindowPVRBase(bRadio, bRadio ? WINDOW_RADIO_RECORDINGS : WINDOW_TV_RECORDINGS, "MyPVRRecordings.xml") ,
+  m_bShowDeletedRecordings(false)
 {
 }
 
@@ -69,15 +70,18 @@ void CGUIWindowPVRRecordings::OnWindowLoaded()
 
 std::string CGUIWindowPVRRecordings::GetDirectoryPath(void)
 {
-  if (StringUtils::StartsWith(m_vecItems->GetPath(), "pvr://recordings/"))
+  std::string basePath = StringUtils::Format("pvr://recordings/%s/", m_bShowDeletedRecordings ? "deleted" : "active");
+
+  if (StringUtils::StartsWith(m_vecItems->GetPath(), basePath))
     return m_vecItems->GetPath();
-  return "pvr://recordings/";
+
+  return basePath;
 }
 
 std::string CGUIWindowPVRRecordings::GetResumeString(const CFileItem& item)
 {
   std::string resumeString;
-  if (item.IsPVRRecording())
+  if (item.IsUsablePVRRecording())
   {
 
     // First try to find the resume position on the back-end, if that fails use video database
@@ -109,40 +113,62 @@ void CGUIWindowPVRRecordings::GetContextButtons(int itemNumber, CContextButtons
     return;
   CFileItemPtr pItem = m_vecItems->Get(itemNumber);
 
+  bool isDeletedRecording = false;
+
   if (pItem->HasPVRRecordingInfoTag())
   {
+    isDeletedRecording = pItem->GetPVRRecordingInfoTag()->IsDeleted();
+
     buttons.Add(CONTEXT_BUTTON_INFO, 19053);      /* Get Information of this recording */
-    buttons.Add(CONTEXT_BUTTON_FIND, 19003);      /* Find similar program */
-    buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); /* Play this recording */
-    std::string resumeString = GetResumeString(*pItem);
-    if (!resumeString.empty())
+    if (!isDeletedRecording)
     {
-      buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
+      buttons.Add(CONTEXT_BUTTON_FIND, 19003);      /* Find similar program */
+      buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); /* Play this recording */
+      std::string resumeString = GetResumeString(*pItem);
+      if (!resumeString.empty())
+      {
+        buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
+      }
+    }
+    else
+    {
+      buttons.Add(CONTEXT_BUTTON_UNDELETE, 19290);    /* Undelete this recording */
+      buttons.Add(CONTEXT_BUTTON_DELETE, 19291);      /* Delete this permanently */
+      if (m_vecItems->GetObjectCount() > 1)
+        buttons.Add(CONTEXT_BUTTON_DELETE_ALL, 19292);  /* Delete all permanently */
     }
   }
-  if (pItem->m_bIsFolder)
-  {
-    // Have both options for folders since we don't know whether all childs are watched/unwatched
-    buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */
-    buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   /* Mark as Watched */
-  }
-  if (pItem->HasPVRRecordingInfoTag())
+  if (!isDeletedRecording)
   {
-    if (pItem->GetPVRRecordingInfoTag()->m_playCount > 0)
+    if (pItem->m_bIsFolder)
+    {
+      // Have both options for folders since we don't know whether all childs are watched/unwatched
       buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */
-    else
       buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   /* Mark as Watched */
+    }
+    if (pItem->HasPVRRecordingInfoTag())
+    {
+      if (pItem->GetPVRRecordingInfoTag()->m_playCount > 0)
+        buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */
+      else
+        buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103);   /* Mark as Watched */
 
-    buttons.Add(CONTEXT_BUTTON_RENAME, 118);      /* Rename this recording */
+      buttons.Add(CONTEXT_BUTTON_RENAME, 118);      /* Rename this recording */
+    }
+
+    buttons.Add(CONTEXT_BUTTON_DELETE, 117);
   }
-  
-  buttons.Add(CONTEXT_BUTTON_DELETE, 117);
 
-  if (pItem->HasPVRRecordingInfoTag() &&
-      g_PVRClients->HasMenuHooks(pItem->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_RECORDING))
-    buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);      /* PVR client specific action */
+  if (pItem->HasPVRRecordingInfoTag())
+  {
+    if (!isDeletedRecording && g_PVRClients->HasMenuHooks(pItem->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_RECORDING))
+      buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);      /* PVR client specific action */
+    else if (isDeletedRecording && g_PVRClients->HasMenuHooks(pItem->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_DELETED_RECORDING))
+      buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195);      /* PVR client specific action */
+  }
 
-  CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons);
+  if (!isDeletedRecording)
+    CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons);
 }
 
 bool CGUIWindowPVRRecordings::OnAction(const CAction &action)
@@ -150,7 +176,7 @@ bool CGUIWindowPVRRecordings::OnAction(const CAction &action)
   if (action.GetID() == ACTION_PARENT_DIR ||
       action.GetID() == ACTION_NAV_BACK)
   {
-    if (m_vecItems->GetPath() != "pvr://recordings/")
+    if (m_vecItems->GetPath() != "pvr://recordings/active/" && m_vecItems->GetPath() != "pvr://recordings/deleted/")
     {
       GoParentFolder();
       return true;
@@ -168,6 +194,8 @@ bool CGUIWindowPVRRecordings::OnContextButton(int itemNumber, CONTEXT_BUTTON but
   return OnContextButtonPlay(pItem.get(), button) ||
       OnContextButtonRename(pItem.get(), button) ||
       OnContextButtonDelete(pItem.get(), button) ||
+      OnContextButtonUndelete(pItem.get(), button) ||
+      OnContextButtonDeleteAll(pItem.get(), button) ||
       OnContextButtonInfo(pItem.get(), button) ||
       OnContextButtonMarkWatched(pItem, button) ||
       CGUIWindowPVRBase::OnContextButton(itemNumber, button);
@@ -177,7 +205,30 @@ bool CGUIWindowPVRRecordings::Update(const std::string &strDirectory, bool updat
 {
   m_thumbLoader.StopThread();
 
-  return CGUIWindowPVRBase::Update(strDirectory);
+  bool bReturn = CGUIWindowPVRBase::Update(strDirectory);
+
+  /* empty list for deleted recordings */
+  if (m_vecItems->GetObjectCount() == 0 && m_bShowDeletedRecordings)
+  {
+    /* show the normal recordings instead */
+    m_bShowDeletedRecordings = false;
+    Update(GetDirectoryPath());
+  }
+
+  return bReturn;
+}
+
+void CGUIWindowPVRRecordings::UpdateButtons(void)
+{
+  CGUIRadioButtonControl *btnShowDeleted = (CGUIRadioButtonControl*) GetControl(CONTROL_BTNSHOWDELETED);
+  if (btnShowDeleted)
+  {
+    btnShowDeleted->SetVisible(g_PVRRecordings->HasDeletedRecordings());
+    btnShowDeleted->SetSelected(m_bShowDeletedRecordings);
+  }
+
+  CGUIWindowPVRBase::UpdateButtons();
+  SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, m_bShowDeletedRecordings ? g_localizeStrings.Get(19179) : ""); /* Deleted recordings trash */
 }
 
 bool CGUIWindowPVRRecordings::OnMessage(CGUIMessage &message)
@@ -236,6 +287,16 @@ bool CGUIWindowPVRRecordings::OnMessage(CGUIMessage &message)
         g_PVRRecordings->SetGroupItems(radioButton->IsSelected());
         Refresh(true);
       }
+      else if (message.GetSenderId() == CONTROL_BTNSHOWDELETED)
+      {
+        CGUIRadioButtonControl *radioButton = (CGUIRadioButtonControl*) GetControl(CONTROL_BTNSHOWDELETED);
+        if (radioButton)
+        {
+          m_bShowDeletedRecordings = radioButton->IsSelected();
+          Update(GetDirectoryPath());
+        }
+        bReturn = true;
+      }
       break;
     case GUI_MSG_REFRESH_LIST:
       switch(message.GetParam1())
@@ -276,7 +337,7 @@ bool CGUIWindowPVRRecordings::ActionDeleteRecording(CFileItem *item)
     return bReturn;
 
   pDialog->SetHeading(122); // Confirm delete
-  pDialog->SetLine(0, item->m_bIsFolder ? 19113 : 19112); // Are you sure?
+  pDialog->SetLine(0, item->m_bIsFolder ? 19113 : item->GetPVRRecordingInfoTag()->IsDeleted() ? 19294 : 19112); // Delete all recordings in this folder? / Delete this recording permanently? / Delete this recording?
   pDialog->SetLine(1, "");
   pDialog->SetLine(2, item->GetLabel());
   pDialog->SetChoice(1, 117); // Delete
@@ -297,7 +358,8 @@ bool CGUIWindowPVRRecordings::ActionDeleteRecording(CFileItem *item)
     m_vecItems->Remove(item);
 
     /* go to the parent folder if we're in a subdirectory and just deleted the last item */
-    if (m_vecItems->GetPath() != "pvr://recordings/" && m_vecItems->GetObjectCount() == 0)
+    if (m_vecItems->GetPath() != "pvr://recordings/active/" &&
+        m_vecItems->GetPath() != "pvr://recordings/deleted/" && m_vecItems->GetObjectCount() == 0)
       GoParentFolder();
   }
 
@@ -309,6 +371,72 @@ bool CGUIWindowPVRRecordings::OnContextButtonDelete(CFileItem *item, CONTEXT_BUT
   return button == CONTEXT_BUTTON_DELETE ? ActionDeleteRecording(item) : false;
 }
 
+bool CGUIWindowPVRRecordings::OnContextButtonUndelete(CFileItem *item, CONTEXT_BUTTON button)
+{
+  bool bReturn = false;
+
+  if (button != CONTEXT_BUTTON_UNDELETE || !item->IsDeletedPVRRecording())
+    return bReturn;
+
+  /* undelete the recording */
+  if (g_PVRRecordings->Undelete(*item))
+  {
+    g_PVRManager.TriggerRecordingsUpdate();
+    bReturn = true;
+
+    /* remove the item from the list immediately, otherwise the
+    item count further down may be wrong */
+    m_vecItems->Remove(item);
+
+    /* go to the parent folder if we're in a subdirectory and just deleted the last item */
+    if (m_vecItems->GetPath() != "pvr://recordings/deleted/" && m_vecItems->GetObjectCount() == 0)
+      GoParentFolder();
+  }
+
+  return bReturn;
+}
+
+bool CGUIWindowPVRRecordings::OnContextButtonDeleteAll(CFileItem *item, CONTEXT_BUTTON button)
+{
+  bool bReturn = false;
+
+  if (button != CONTEXT_BUTTON_DELETE_ALL || !item->IsDeletedPVRRecording())
+    return bReturn;
+
+  /* show a confirmation dialog */
+  CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
+  if (!pDialog)
+    return bReturn;
+
+
+  pDialog->SetHeading(19292); // Delete all permanently
+  pDialog->SetLine(0, 19293); // Delete all recordings permanently?
+  pDialog->SetLine(1, "");
+  pDialog->SetLine(2, "");
+  pDialog->SetChoice(1, 117); // Delete
+
+  /* prompt for the user's confirmation */
+  pDialog->DoModal();
+  if (!pDialog->IsConfirmed())
+    return bReturn;
+
+  /* undelete the recording */
+  if (g_PVRRecordings->DeleteAllRecordingsFromTrash())
+  {
+    g_PVRManager.TriggerRecordingsUpdate();
+    bReturn = true;
+
+    /* remove the item from the list immediately, otherwise the
+    item count further down may be wrong */
+    m_vecItems->Clear();
+
+    /* go to the parent folder if we're in a subdirectory and just deleted the last item */
+    if (m_vecItems->GetPath() != "pvr://recordings/deleted/" && m_vecItems->GetObjectCount() == 0)
+      GoParentFolder();
+  }
+  return bReturn;
+}
+
 bool CGUIWindowPVRRecordings::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
 {
   bool bReturn = false;
index 72ba2e8..6d45ca0 100644 (file)
@@ -40,6 +40,7 @@ namespace PVR
     void GetContextButtons(int itemNumber, CContextButtons &buttons);
     bool OnContextButton(int itemNumber, CONTEXT_BUTTON button);
     bool Update(const std::string &strDirectory, bool updateFilterPath = true);
+    void UpdateButtons(void);
     void UnregisterObservers(void);
     void ResetObservers(void);
 
@@ -50,6 +51,8 @@ namespace PVR
   private:
     bool ActionDeleteRecording(CFileItem *item);
     bool OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button);
+    bool OnContextButtonUndelete(CFileItem *item, CONTEXT_BUTTON button);
+    bool OnContextButtonDeleteAll(CFileItem *item, CONTEXT_BUTTON button);
     bool OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button);
     bool OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button);
     bool OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button);
@@ -57,5 +60,6 @@ namespace PVR
 
     CVideoThumbLoader m_thumbLoader;
     CVideoDatabase m_database;
+    bool m_bShowDeletedRecordings;
   };
 }
index bfbbaf1..bcac15a 100644 (file)
@@ -116,7 +116,7 @@ bool CGUIWindowPVRSearch::OnContextButton(const CFileItem &item, CONTEXT_BUTTON
         if (tag)
           m_searchfilter.m_strSearchTerm = "\"" + tag->Title() + "\"";
       }
-      else if (item.IsPVRRecording())
+      else if (item.IsUsablePVRRecording())
         m_searchfilter.m_strSearchTerm = "\"" + item.GetPVRRecordingInfoTag()->m_strTitle + "\"";
       else if (item.IsPVRTimer())
         m_searchfilter.m_strSearchTerm = "\"" + item.GetPVRTimerInfoTag()->m_strTitle + "\"";
index c78b5bd..0547b8a 100644 (file)
@@ -1410,7 +1410,7 @@ bool CGUIWindowVideoBase::OnPlayMedia(int iItem)
   }
   CLog::Log(LOGDEBUG, "%s %s", __FUNCTION__, CURL::GetRedacted(item.GetPath()).c_str());
 
-  if (StringUtils::StartsWith(item.GetPath(), "pvr://recordings/"))
+  if (StringUtils::StartsWith(item.GetPath(), "pvr://recordings/active/"))
   {
     if (!g_PVRManager.IsStarted())
       return false;
index 9e281b5..68f3a43 100644 (file)
@@ -182,6 +182,7 @@ void CGUIWindowSystemInfo::FrameMove()
     SetControlLabel(i++, "%s: %s", 19116, PVR_BACKEND_DISKSPACE);
     SetControlLabel(i++, "%s: %s", 19019, PVR_BACKEND_CHANNELS);
     SetControlLabel(i++, "%s: %s", 19163, PVR_BACKEND_RECORDINGS);
+    SetControlLabel(i++, "%s: %s", 19168, PVR_BACKEND_DELETED_RECORDINGS); // Deleted and recoverable recordings
     SetControlLabel(i++, "%s: %s", 19025, PVR_BACKEND_TIMERS);
   }