2 * Copyright (C) 2012-2013 Team XBMC
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)
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.
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/>.
21 #include "GUIWindowPVRRecordings.h"
23 #include "guilib/GUIKeyboardFactory.h"
24 #include "dialogs/GUIDialogYesNo.h"
25 #include "guilib/GUIWindowManager.h"
26 #include "guilib/Key.h"
27 #include "guilib/LocalizeStrings.h"
28 #include "GUIInfoManager.h"
29 #include "pvr/PVRManager.h"
30 #include "pvr/recordings/PVRRecordings.h"
31 #include "pvr/timers/PVRTimers.h"
32 #include "pvr/windows/GUIWindowPVR.h"
33 #include "utils/log.h"
34 #include "utils/StringUtils.h"
35 #include "threads/SingleLock.h"
36 #include "pvr/addons/PVRClients.h"
37 #include "video/windows/GUIWindowVideoNav.h"
41 CGUIWindowPVRRecordings::CGUIWindowPVRRecordings(CGUIWindowPVR *parent) :
42 CGUIWindowPVRCommon(parent, PVR_WINDOW_RECORDINGS, CONTROL_BTNRECORDINGS, CONTROL_LIST_RECORDINGS)
44 m_strSelectedPath = "pvr://recordings/";
47 void CGUIWindowPVRRecordings::UnregisterObservers(void)
49 CSingleLock lock(m_critSection);
51 g_PVRRecordings->UnregisterObserver(this);
53 g_PVRTimers->UnregisterObserver(this);
54 g_infoManager.UnregisterObserver(this);
57 void CGUIWindowPVRRecordings::ResetObservers(void)
59 CSingleLock lock(m_critSection);
60 g_PVRRecordings->RegisterObserver(this);
61 g_PVRTimers->RegisterObserver(this);
62 g_infoManager.RegisterObserver(this);
65 CStdString CGUIWindowPVRRecordings::GetResumeString(const CFileItem& item)
67 CStdString resumeString;
68 if (item.IsPVRRecording())
71 // First try to find the resume position on the back-end, if that fails use video database
72 int positionInSeconds = item.GetPVRRecordingInfoTag()->GetLastPlayedPosition();
73 // If the back-end does report a saved position then make sure there is a corresponding resume bookmark
74 if (positionInSeconds > 0)
77 bookmark.timeInSeconds = positionInSeconds;
78 bookmark.totalTimeInSeconds = (double)item.GetPVRRecordingInfoTag()->GetDuration();
82 CStdString itemPath(item.GetPVRRecordingInfoTag()->m_strFileNameAndPath);
83 db.AddBookMarkToFile(itemPath, bookmark, CBookmark::RESUME);
87 else if (positionInSeconds < 0)
93 CStdString itemPath(item.GetPVRRecordingInfoTag()->m_strFileNameAndPath);
94 if (db.GetResumeBookMark(itemPath, bookmark) )
95 positionInSeconds = lrint(bookmark.timeInSeconds);
100 // Suppress resume from 0
101 if (positionInSeconds > 0)
102 resumeString = StringUtils::Format(g_localizeStrings.Get(12022).c_str(), StringUtils::SecondsToTimeString(positionInSeconds).c_str());
107 void CGUIWindowPVRRecordings::GetContextButtons(int itemNumber, CContextButtons &buttons) const
109 if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
111 CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
113 if (pItem->HasPVRRecordingInfoTag())
115 buttons.Add(CONTEXT_BUTTON_INFO, 19053); /* Get Information of this recording */
116 buttons.Add(CONTEXT_BUTTON_FIND, 19003); /* Find similar program */
117 buttons.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021); /* Play this recording */
118 CStdString resumeString = GetResumeString(*pItem);
119 if (!resumeString.empty())
121 buttons.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
124 if (pItem->m_bIsFolder)
126 // Have both options for folders since we don't know whether all childs are watched/unwatched
127 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */
128 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); /* Mark as Watched */
130 if (pItem->HasPVRRecordingInfoTag())
132 if (pItem->GetPVRRecordingInfoTag()->m_playCount > 0)
133 buttons.Add(CONTEXT_BUTTON_MARK_UNWATCHED, 16104); /* Mark as UnWatched */
135 buttons.Add(CONTEXT_BUTTON_MARK_WATCHED, 16103); /* Mark as Watched */
137 buttons.Add(CONTEXT_BUTTON_RENAME, 118); /* Rename this recording */
138 buttons.Add(CONTEXT_BUTTON_DELETE, 117); /* Delete this recording */
140 buttons.Add(CONTEXT_BUTTON_SORTBY_NAME, 103); /* sort by name */
141 buttons.Add(CONTEXT_BUTTON_SORTBY_DATE, 104); /* sort by date */
143 if (pItem->HasPVRRecordingInfoTag() &&
144 g_PVRClients->HasMenuHooks(pItem->GetPVRRecordingInfoTag()->m_iClientId, PVR_MENUHOOK_RECORDING))
145 buttons.Add(CONTEXT_BUTTON_MENU_HOOKS, 19195); /* PVR client specific action */
147 // Update sort by button
148 //if (m_guiState->GetSortMethod()!=SortByNone)
150 // CStdString sortLabel;
151 // sortLabel.Format(g_localizeStrings.Get(550).c_str(), g_localizeStrings.Get(m_guiState->GetSortMethodLabel()).c_str());
152 // buttons.Add(CONTEXT_BUTTON_SORTBY, sortLabel); /* Sort method */
154 // if (m_guiState->GetDisplaySortOrder()==SortOrderAscending)
155 // buttons.Add(CONTEXT_BUTTON_SORTASC, 584); /* Sort up or down */
157 // buttons.Add(CONTEXT_BUTTON_SORTASC, 585); /* Sort up or down */
161 bool CGUIWindowPVRRecordings::OnAction(const CAction &action)
163 if (action.GetID() == ACTION_PARENT_DIR ||
164 action.GetID() == ACTION_NAV_BACK)
166 if (m_parent->m_vecItems->GetPath() != "pvr://recordings/")
167 m_parent->GoParentFolder();
168 else if (action.GetID() == ACTION_NAV_BACK)
169 g_windowManager.PreviousWindow();
174 return CGUIWindowPVRCommon::OnAction(action);
177 bool CGUIWindowPVRRecordings::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
179 if (itemNumber < 0 || itemNumber >= m_parent->m_vecItems->Size())
181 CFileItemPtr pItem = m_parent->m_vecItems->Get(itemNumber);
183 return OnContextButtonPlay(pItem.get(), button) ||
184 OnContextButtonRename(pItem.get(), button) ||
185 OnContextButtonDelete(pItem.get(), button) ||
186 OnContextButtonInfo(pItem.get(), button) ||
187 OnContextButtonMarkWatched(pItem, button) ||
188 CGUIWindowPVRCommon::OnContextButton(itemNumber, button);
191 void CGUIWindowPVRRecordings::OnWindowUnload(void)
193 m_strSelectedPath = m_parent->m_vecItems->GetPath();
194 CGUIWindowPVRCommon::OnWindowUnload();
197 void CGUIWindowPVRRecordings::UpdateData(bool bUpdateSelectedFile /* = true */)
199 CSingleLock lock(m_critSection);
200 CLog::Log(LOGDEBUG, "CGUIWindowPVRRecordings - %s - update window '%s'. set view to %d", __FUNCTION__, GetName(), m_iControlList);
201 m_bUpdateRequired = false;
203 /* lock the graphics context while updating */
204 CSingleLock graphicsLock(g_graphicsContext);
206 m_iSelected = m_parent->m_viewControl.GetSelectedItem();
207 if (!StringUtils::StartsWith(m_parent->m_vecItems->GetPath(), "pvr://recordings/"))
208 m_strSelectedPath = "pvr://recordings/";
210 m_strSelectedPath = m_parent->m_vecItems->GetPath();
212 m_parent->m_viewControl.SetCurrentView(m_iControlList);
214 m_parent->m_vecItems->Clear();
215 m_parent->m_vecItems->SetPath(m_strSelectedPath);
216 m_parent->Update(m_strSelectedPath);
217 m_parent->m_viewControl.SetItems(*m_parent->m_vecItems);
219 if (bUpdateSelectedFile)
221 if (!SelectPlayingFile())
222 m_parent->m_viewControl.SetSelectedItem(m_iSelected);
225 m_parent->SetLabel(CONTROL_LABELHEADER, g_localizeStrings.Get(19017));
226 m_parent->SetLabel(CONTROL_LABELGROUP, "");
229 void CGUIWindowPVRRecordings::Notify(const Observable &obs, const ObservableMessage msg)
231 if (msg == ObservableMessageTimers || msg == ObservableMessageCurrentItem)
236 m_bUpdateRequired = true;
238 else if (msg == ObservableMessageRecordings || msg == ObservableMessageTimersReset)
243 m_bUpdateRequired = true;
247 bool CGUIWindowPVRRecordings::OnClickButton(CGUIMessage &message)
249 bool bReturn = false;
251 if (IsSelectedButton(message))
254 g_PVRManager.TriggerRecordingsUpdate();
260 bool CGUIWindowPVRRecordings::OnClickList(CGUIMessage &message)
262 bool bReturn = false;
264 if (IsSelectedList(message))
267 int iAction = message.GetParam1();
268 int iItem = m_parent->m_viewControl.GetSelectedItem();
270 /* get the fileitem pointer */
271 if (iItem < 0 || iItem >= (int) m_parent->m_vecItems->Size())
273 CFileItemPtr pItem = m_parent->m_vecItems->Get(iItem);
275 /* process actions */
276 if (iAction == ACTION_SELECT_ITEM || iAction == ACTION_MOUSE_LEFT_CLICK || iAction == ACTION_PLAY)
278 int choice = CONTEXT_BUTTON_PLAY_ITEM;
279 CStdString resumeString = GetResumeString(*pItem);
280 if (!resumeString.empty())
282 CContextButtons choices;
283 choices.Add(CONTEXT_BUTTON_RESUME_ITEM, resumeString);
284 choices.Add(CONTEXT_BUTTON_PLAY_ITEM, 12021);
285 choice = CGUIDialogContextMenu::ShowAndGetChoice(choices);
290 bReturn = OnContextButtonPlay(pItem.get(), (CONTEXT_BUTTON)choice);
292 else if (iAction == ACTION_CONTEXT_MENU || iAction == ACTION_MOUSE_RIGHT_CLICK)
293 m_parent->OnPopupMenu(iItem);
294 else if (iAction == ACTION_SHOW_INFO)
295 ShowRecordingInfo(pItem.get());
296 else if (iAction == ACTION_DELETE_ITEM)
297 bReturn = ActionDeleteRecording(pItem.get());
305 bool CGUIWindowPVRRecordings::OnContextButtonDelete(CFileItem *item, CONTEXT_BUTTON button)
307 bool bReturn = false;
309 if (button == CONTEXT_BUTTON_DELETE)
313 CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO);
316 pDialog->SetHeading(122);
317 pDialog->SetLine(0, 19043);
318 pDialog->SetLine(1, "");
319 pDialog->SetLine(2, item->GetPVRRecordingInfoTag()->m_strTitle);
322 if (!pDialog->IsConfirmed())
325 bReturn = g_PVRRecordings->DeleteRecording(*item);
331 bool CGUIWindowPVRRecordings::OnContextButtonInfo(CFileItem *item, CONTEXT_BUTTON button)
333 bool bReturn = false;
335 if (button == CONTEXT_BUTTON_INFO)
338 ShowRecordingInfo(item);
344 bool CGUIWindowPVRRecordings::OnContextButtonPlay(CFileItem *item, CONTEXT_BUTTON button)
346 bool bReturn = false;
348 if ((button == CONTEXT_BUTTON_PLAY_ITEM) ||
349 (button == CONTEXT_BUTTON_RESUME_ITEM))
351 item->m_lStartOffset = button == CONTEXT_BUTTON_RESUME_ITEM ? STARTOFFSET_RESUME : 0;
352 bReturn = PlayFile(item, false); /* play recording */
358 bool CGUIWindowPVRRecordings::OnContextButtonRename(CFileItem *item, CONTEXT_BUTTON button)
360 bool bReturn = false;
362 if (button == CONTEXT_BUTTON_RENAME)
366 CPVRRecording *recording = item->GetPVRRecordingInfoTag();
367 CStdString strNewName = recording->m_strTitle;
368 if (CGUIKeyboardFactory::ShowAndGetInput(strNewName, g_localizeStrings.Get(19041), false))
370 if (g_PVRRecordings->RenameRecording(*item, strNewName))
378 bool CGUIWindowPVRRecordings::OnContextButtonMarkWatched(const CFileItemPtr &item, CONTEXT_BUTTON button)
380 bool bReturn = false;
382 if (button == CONTEXT_BUTTON_MARK_WATCHED)
386 int newSelection = m_parent->m_viewControl.GetSelectedItem();
387 g_PVRRecordings->SetRecordingsPlayCount(item, 1);
388 m_parent->m_viewControl.SetSelectedItem(newSelection);
393 if (button == CONTEXT_BUTTON_MARK_UNWATCHED)
397 g_PVRRecordings->SetRecordingsPlayCount(item, 0);
405 void CGUIWindowPVRRecordings::BeforeUpdate(const CStdString &strDirectory)
407 if (m_thumbLoader.IsLoading())
408 m_thumbLoader.StopThread();
411 void CGUIWindowPVRRecordings::AfterUpdate(CFileItemList& items)
413 if (!items.IsEmpty())
416 for (int i = 0; i < items.Size(); i++)
418 CFileItemPtr pItem = items[i];
419 if (!pItem->m_bIsFolder)
423 if (!files.IsEmpty())
425 files.SetPath(items.GetPath());
426 if(m_database.Open())
428 if (g_PVRRecordings->HasAllRecordingsPathExtension(files.GetPath()))
430 // Build a map of all files belonging to common subdirectories and call
431 // LoadVideoInfo for each item list
432 typedef boost::shared_ptr<CFileItemList> CFileItemListPtr;
433 typedef std::map<CStdString, CFileItemListPtr> DirectoryMap;
435 DirectoryMap directory_map;
436 for (int i = 0; i < files.Size(); i++)
438 CStdString strDirectory = URIUtils::GetDirectory(files[i]->GetPath());
439 DirectoryMap::iterator it = directory_map.find(strDirectory);
440 if (it == directory_map.end())
441 it = directory_map.insert(std::make_pair(
442 strDirectory, CFileItemListPtr(new CFileItemList(strDirectory)))).first;
443 it->second->Add(files[i]);
446 for (DirectoryMap::iterator it = directory_map.begin(); it != directory_map.end(); it++)
447 CGUIWindowVideoNav::LoadVideoInfo(*it->second, m_database, false);
450 CGUIWindowVideoNav::LoadVideoInfo(files, m_database, false);
453 m_thumbLoader.Load(files);