Merge pull request #26273 from 78andyp/blurayfixes2
[xbmc.git] / xbmc / pvr / windows / GUIWindowPVRSearch.cpp
blob50504e46284895d4a0e189b9b5ec3d6b3203a5f0
1 /*
2 * Copyright (C) 2012-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIWindowPVRSearch.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "dialogs/GUIDialogBusy.h"
15 #include "dialogs/GUIDialogYesNo.h"
16 #include "guilib/GUIComponent.h"
17 #include "guilib/GUIMessage.h"
18 #include "guilib/GUIWindowManager.h"
19 #include "guilib/LocalizeStrings.h"
20 #include "input/actions/Action.h"
21 #include "input/actions/ActionIDs.h"
22 #include "messaging/helpers/DialogOKHelper.h"
23 #include "pvr/PVRItem.h"
24 #include "pvr/PVRManager.h"
25 #include "pvr/dialogs/GUIDialogPVRGuideSearch.h"
26 #include "pvr/epg/Epg.h"
27 #include "pvr/epg/EpgContainer.h"
28 #include "pvr/epg/EpgInfoTag.h"
29 #include "pvr/epg/EpgSearch.h"
30 #include "pvr/epg/EpgSearchFilter.h"
31 #include "pvr/epg/EpgSearchPath.h"
32 #include "pvr/guilib/PVRGUIActionsEPG.h"
33 #include "pvr/guilib/PVRGUIActionsTimers.h"
34 #include "pvr/recordings/PVRRecording.h"
35 #include "threads/IRunnable.h"
36 #include "utils/StringUtils.h"
37 #include "utils/URIUtils.h"
38 #include "utils/Variant.h"
40 #include <algorithm>
41 #include <memory>
42 #include <vector>
44 using namespace PVR;
45 using namespace KODI::MESSAGING;
47 namespace
49 class AsyncSearchAction : private IRunnable
51 public:
52 AsyncSearchAction() = delete;
53 AsyncSearchAction(CFileItemList* items, CPVREpgSearchFilter* filter)
54 : m_items(items), m_filter(filter)
57 bool Execute();
59 private:
60 // IRunnable implementation
61 void Run() override;
63 CFileItemList* m_items;
64 CPVREpgSearchFilter* m_filter;
67 bool AsyncSearchAction::Execute()
69 CGUIDialogBusy::Wait(this, 100, false);
70 return true;
73 void AsyncSearchAction::Run()
75 CPVREpgSearch search(*m_filter);
76 search.Execute();
77 const auto tags{search.GetResults()};
78 for (const auto& tag : tags)
80 m_items->Add(std::make_shared<CFileItem>(tag));
83 } // unnamed namespace
85 CGUIWindowPVRSearchBase::CGUIWindowPVRSearchBase(bool bRadio, int id, const std::string& xmlFile)
86 : CGUIWindowPVRBase(bRadio, id, xmlFile)
90 CGUIWindowPVRSearchBase::~CGUIWindowPVRSearchBase()
94 void CGUIWindowPVRSearchBase::GetContextButtons(int itemNumber, CContextButtons& buttons)
96 if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
97 return;
99 const CPVREpgSearchPath path(m_vecItems->GetPath());
100 const bool bIsSavedSearchesRoot = (path.IsValid() && path.IsSavedSearchesRoot());
101 if (!bIsSavedSearchesRoot)
102 buttons.Add(CONTEXT_BUTTON_CLEAR, 19232); // "Clear search results"
104 CGUIWindowPVRBase::GetContextButtons(itemNumber, buttons);
107 bool CGUIWindowPVRSearchBase::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
109 if (itemNumber < 0 || itemNumber >= m_vecItems->Size())
110 return false;
111 CFileItemPtr pItem = m_vecItems->Get(itemNumber);
113 return OnContextButtonClear(pItem.get(), button) ||
114 CGUIMediaWindow::OnContextButton(itemNumber, button);
117 void CGUIWindowPVRSearchBase::SetItemToSearch(const CFileItem& item)
119 if (item.HasEPGSearchFilter())
121 SetSearchFilter(item.GetEPGSearchFilter());
123 else if (item.IsUsablePVRRecording())
125 SetSearchFilter(std::make_shared<CPVREpgSearchFilter>(m_bRadio));
126 m_searchfilter->SetSearchPhrase(item.GetPVRRecordingInfoTag()->m_strTitle);
128 else
130 SetSearchFilter(std::make_shared<CPVREpgSearchFilter>(m_bRadio));
132 const std::shared_ptr<const CPVREpgInfoTag> epgTag(CPVRItem(item).GetEpgInfoTag());
133 if (epgTag && !CServiceBroker::GetPVRManager().IsParentalLocked(epgTag))
134 m_searchfilter->SetSearchPhrase(epgTag->Title());
137 ExecuteSearch();
140 void CGUIWindowPVRSearchBase::OnPrepareFileItems(CFileItemList& items)
142 if (m_bSearchConfirmed)
144 items.Clear();
147 if (items.IsEmpty())
149 auto item = std::make_shared<CFileItem>(CPVREpgSearchPath::PATH_SEARCH_DIALOG, false);
150 item->SetLabel(g_localizeStrings.Get(
151 m_searchfilter == nullptr ? 19335 : 19336)); // "New search..." / "Edit search..."
152 item->SetLabelPreformatted(true);
153 item->SetSpecialSort(SortSpecialOnTop);
154 item->SetArt("icon", "DefaultPVRSearch.png");
155 items.Add(item);
157 item = std::make_shared<CFileItem>(m_bRadio ? CPVREpgSearchPath::PATH_RADIO_SAVEDSEARCHES
158 : CPVREpgSearchPath::PATH_TV_SAVEDSEARCHES,
159 true);
160 item->SetLabel(g_localizeStrings.Get(19337)); // "Saved searches"
161 item->SetLabelPreformatted(true);
162 item->SetSpecialSort(SortSpecialOnTop);
163 item->SetArt("icon", "DefaultFolder.png");
164 items.Add(item);
167 if (m_bSearchConfirmed)
169 const int itemCount = items.GetObjectCount();
171 AsyncSearchAction(&items, m_searchfilter.get()).Execute();
173 if (items.GetObjectCount() == itemCount)
175 HELPERS::ShowOKDialogText(CVariant{284}, // "No results found"
176 m_searchfilter->GetSearchTerm());
181 bool CGUIWindowPVRSearchBase::OnAction(const CAction& action)
183 if (action.GetID() == ACTION_PARENT_DIR || action.GetID() == ACTION_NAV_BACK)
185 const CPVREpgSearchPath path(m_vecItems->GetPath());
186 if (path.IsValid() && path.IsSavedSearchesRoot())
188 // Go to root dir and show previous search results if any
189 m_bSearchConfirmed = (m_searchfilter != nullptr);
190 GoParentFolder();
191 return true;
194 return CGUIWindowPVRBase::OnAction(action);
197 bool CGUIWindowPVRSearchBase::OnMessage(CGUIMessage& message)
199 if (message.GetMessage() == GUI_MSG_CLICKED)
201 if (message.GetSenderId() == m_viewControl.GetCurrentControl())
203 int iItem = m_viewControl.GetSelectedItem();
204 if (iItem >= 0 && iItem < m_vecItems->Size())
206 CFileItemPtr pItem = m_vecItems->Get(iItem);
208 /* process actions */
209 switch (message.GetParam1())
211 case ACTION_SHOW_INFO:
212 case ACTION_SELECT_ITEM:
213 case ACTION_MOUSE_LEFT_CLICK:
215 const CPVREpgSearchPath path(pItem->GetPath());
216 const bool bIsSavedSearch = (path.IsValid() && path.IsSavedSearch());
217 const bool bIsSavedSearchesRoot = (path.IsValid() && path.IsSavedSearchesRoot());
219 if (message.GetParam1() != ACTION_SHOW_INFO)
221 if (pItem->IsParentFolder())
223 // Go to root dir and show previous search results if any
224 m_bSearchConfirmed = (m_searchfilter != nullptr);
225 break; // handled by base class
228 if (bIsSavedSearchesRoot)
230 // List saved searches
231 m_bSearchConfirmed = false;
232 break; // handled by base class
235 if (bIsSavedSearch)
237 // Execute selected saved search
238 SetSearchFilter(pItem->GetEPGSearchFilter());
239 ExecuteSearch();
240 return true;
244 if (bIsSavedSearch)
246 OpenDialogSearch(*pItem);
248 else if (pItem->GetPath() == CPVREpgSearchPath::PATH_SEARCH_DIALOG)
250 OpenDialogSearch(m_searchfilter);
252 else
254 CServiceBroker::GetPVRManager().Get<PVR::GUI::EPG>().ShowEPGInfo(*pItem);
256 return true;
259 case ACTION_CONTEXT_MENU:
260 case ACTION_MOUSE_RIGHT_CLICK:
261 OnPopupMenu(iItem);
262 return true;
264 case ACTION_RECORD:
265 CServiceBroker::GetPVRManager().Get<PVR::GUI::Timers>().ToggleTimer(*pItem);
266 return true;
271 else if (message.GetMessage() == GUI_MSG_REFRESH_LIST)
273 if (static_cast<PVREvent>(message.GetParam1()) == PVREvent::SavedSearchesInvalidated)
275 Refresh(true);
277 // Refresh triggered by deleted saved search?
278 if (m_searchfilter)
280 const CPVREpgSearchPath path(m_vecItems->GetPath());
281 const bool bIsSavedSearchesRoot = (path.IsValid() && path.IsSavedSearchesRoot());
282 if (bIsSavedSearchesRoot)
284 const std::string filterPath = m_searchfilter->GetPath();
285 bool bFound = false;
286 for (const auto& item : *m_vecItems)
288 const auto filter = item->GetEPGSearchFilter();
289 if (filter && filter->GetPath() == filterPath)
291 bFound = true;
292 break;
295 if (!bFound)
296 SetSearchFilter(nullptr);
301 else if (message.GetMessage() == GUI_MSG_WINDOW_INIT)
303 const CPVREpgSearchPath path(message.GetStringParam(0));
304 if (path.IsValid() && path.IsSavedSearch())
306 const std::shared_ptr<CPVREpgSearchFilter> filter =
307 CServiceBroker::GetPVRManager().EpgContainer().GetSavedSearchById(path.IsRadio(),
308 path.GetId());
309 if (filter)
311 SetSearchFilter(filter);
312 m_bSearchConfirmed = true;
317 return CGUIWindowPVRBase::OnMessage(message);
320 bool CGUIWindowPVRSearchBase::Update(const std::string& strDirectory,
321 bool updateFilterPath /* = true */)
323 if (m_vecItems->GetObjectCount() > 0)
325 const CPVREpgSearchPath path(m_vecItems->GetPath());
326 if (path.IsValid() && path.IsSavedSearchesRoot())
328 const std::string oldPath = m_vecItems->GetPath();
330 const bool bReturn = CGUIWindowPVRBase::Update(strDirectory);
332 if (bReturn && oldPath == m_vecItems->GetPath() && m_vecItems->GetObjectCount() == 0)
334 // Go to parent folder if we're in a subdir and for instance just deleted the last item
335 GoParentFolder();
337 return bReturn;
340 return CGUIWindowPVRBase::Update(strDirectory);
343 void CGUIWindowPVRSearchBase::UpdateButtons()
345 CGUIWindowPVRBase::UpdateButtons();
347 bool bSavedSearchesRoot = false;
348 std::string header;
349 const CPVREpgSearchPath path(m_vecItems->GetPath());
350 if (path.IsValid() && path.IsSavedSearchesRoot())
352 bSavedSearchesRoot = true;
353 header = g_localizeStrings.Get(19337); // "Saved searches"
356 if (header.empty() && m_searchfilter)
358 header = m_searchfilter->GetTitle();
359 if (header.empty())
361 header = m_searchfilter->GetSearchTerm();
362 StringUtils::Trim(header, "\"");
365 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER1, header);
367 if (!bSavedSearchesRoot && m_searchfilter && m_searchfilter->IsChanged())
368 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, g_localizeStrings.Get(19342)); // "[not saved]"
369 else if (!bSavedSearchesRoot && m_searchfilter)
370 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, g_localizeStrings.Get(19343)); // "[saved]"
371 else
372 SET_CONTROL_LABEL(CONTROL_LABEL_HEADER2, "");
375 bool CGUIWindowPVRSearchBase::OnContextButtonClear(CFileItem* item, CONTEXT_BUTTON button)
377 bool bReturn = false;
379 if (button == CONTEXT_BUTTON_CLEAR)
381 bReturn = true;
383 m_bSearchConfirmed = false;
384 SetSearchFilter(nullptr);
386 Refresh(true);
389 return bReturn;
392 CGUIDialogPVRGuideSearch::Result CGUIWindowPVRSearchBase::OpenDialogSearch(const CFileItem& item)
394 const auto searchFilter = item.GetEPGSearchFilter();
395 if (!searchFilter)
396 return CGUIDialogPVRGuideSearch::Result::CANCEL;
398 return OpenDialogSearch(searchFilter);
401 CGUIDialogPVRGuideSearch::Result CGUIWindowPVRSearchBase::OpenDialogSearch(
402 const std::shared_ptr<CPVREpgSearchFilter>& searchFilter)
404 CGUIDialogPVRGuideSearch* dlgSearch =
405 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPVRGuideSearch>(
406 WINDOW_DIALOG_PVR_GUIDE_SEARCH);
408 if (!dlgSearch)
409 return CGUIDialogPVRGuideSearch::Result::CANCEL;
411 const std::shared_ptr<CPVREpgSearchFilter> tmpSearchFilter =
412 searchFilter != nullptr ? std::make_shared<CPVREpgSearchFilter>(*searchFilter)
413 : std::make_shared<CPVREpgSearchFilter>(m_bRadio);
415 dlgSearch->SetFilterData(tmpSearchFilter);
417 /* Open dialog window */
418 dlgSearch->Open();
420 const CGUIDialogPVRGuideSearch::Result result = dlgSearch->GetResult();
421 if (result == CGUIDialogPVRGuideSearch::Result::SEARCH)
423 SetSearchFilter(tmpSearchFilter);
424 ExecuteSearch();
426 else if (result == CGUIDialogPVRGuideSearch::Result::SAVE)
428 CServiceBroker::GetPVRManager().EpgContainer().PersistSavedSearch(*tmpSearchFilter);
429 if (searchFilter)
430 searchFilter->SetDatabaseId(tmpSearchFilter->GetDatabaseId());
432 const CPVREpgSearchPath path(m_vecItems->GetPath());
433 if (path.IsValid() && path.IsSearchRoot())
435 SetSearchFilter(tmpSearchFilter);
436 ExecuteSearch();
440 return result;
443 void CGUIWindowPVRSearchBase::ExecuteSearch()
445 m_bSearchConfirmed = true;
447 const CPVREpgSearchPath path(m_vecItems->GetPath());
448 if (path.IsValid() && path.IsSavedSearchesRoot())
450 GoParentFolder();
452 else if (IsActive())
454 Refresh(true);
457 // Save if not a transient search
458 if (m_searchfilter->GetDatabaseId() != PVR_EPG_SEARCH_INVALID_DATABASE_ID)
459 CServiceBroker::GetPVRManager().EpgContainer().UpdateSavedSearchLastExecuted(*m_searchfilter);
462 void CGUIWindowPVRSearchBase::SetSearchFilter(
463 const std::shared_ptr<CPVREpgSearchFilter>& searchFilter)
465 if (m_searchfilter && m_searchfilter->IsChanged() &&
466 (!searchFilter || m_searchfilter->GetPath() != searchFilter->GetPath()))
468 bool bCanceled = false;
469 if (!CGUIDialogYesNo::ShowAndGetInput(CVariant{14117}, // "Warning"
470 CVariant{19341}, // "Save the current search?"
471 CVariant{},
472 CVariant{!m_searchfilter->GetTitle().empty()
473 ? m_searchfilter->GetTitle()
474 : m_searchfilter->GetSearchTerm()},
475 bCanceled, CVariant{107}, // "Yes"
476 CVariant{106}, // "No"
477 CGUIDialogYesNo::NO_TIMEOUT) &&
478 !bCanceled)
480 std::string title = m_searchfilter->GetTitle();
481 if (title.empty())
483 title = m_searchfilter->GetSearchTerm();
484 if (title.empty())
485 title = g_localizeStrings.Get(137); // "Search"
486 else
487 StringUtils::Trim(title, "\"");
489 m_searchfilter->SetTitle(title);
491 CServiceBroker::GetPVRManager().EpgContainer().PersistSavedSearch(*m_searchfilter);
494 m_searchfilter = searchFilter;
497 std::string CGUIWindowPVRTVSearch::GetRootPath() const
499 return CPVREpgSearchPath::PATH_TV_SEARCH;
502 std::string CGUIWindowPVRTVSearch::GetStartFolder(const std::string& dir)
504 return CPVREpgSearchPath::PATH_TV_SEARCH;
507 std::string CGUIWindowPVRTVSearch::GetDirectoryPath()
509 return URIUtils::PathHasParent(m_vecItems->GetPath(), CPVREpgSearchPath::PATH_TV_SEARCH)
510 ? m_vecItems->GetPath()
511 : CPVREpgSearchPath::PATH_TV_SEARCH;
514 std::string CGUIWindowPVRRadioSearch::GetRootPath() const
516 return CPVREpgSearchPath::PATH_RADIO_SEARCH;
519 std::string CGUIWindowPVRRadioSearch::GetStartFolder(const std::string& dir)
521 return CPVREpgSearchPath::PATH_RADIO_SEARCH;
524 std::string CGUIWindowPVRRadioSearch::GetDirectoryPath()
526 return URIUtils::PathHasParent(m_vecItems->GetPath(), CPVREpgSearchPath::PATH_RADIO_SEARCH)
527 ? m_vecItems->GetPath()
528 : CPVREpgSearchPath::PATH_RADIO_SEARCH;