[XAudio2] avoid leak + fix voice creation for closest match
[xbmc.git] / xbmc / pictures / GUIWindowPictures.cpp
blob89d704bef8abb04af3a69acada873077fb93bf5f
1 /*
2 * Copyright (C) 2005-2020 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 "GUIWindowPictures.h"
11 #include "Autorun.h"
12 #include "FileItem.h"
13 #include "FileItemList.h"
14 #include "GUIDialogPictureInfo.h"
15 #include "GUIPassword.h"
16 #include "GUIWindowSlideShow.h"
17 #include "PictureInfoLoader.h"
18 #include "ServiceBroker.h"
19 #include "URL.h"
20 #include "Util.h"
21 #include "addons/gui/GUIDialogAddonInfo.h"
22 #include "application/Application.h"
23 #include "application/ApplicationComponents.h"
24 #include "application/ApplicationPlayer.h"
25 #include "dialogs/GUIDialogMediaSource.h"
26 #include "dialogs/GUIDialogProgress.h"
27 #include "guilib/GUIComponent.h"
28 #include "guilib/GUIWindowManager.h"
29 #include "input/actions/ActionIDs.h"
30 #include "media/MediaLockState.h"
31 #include "messaging/helpers/DialogOKHelper.h"
32 #include "pictures/SlideShowDelegator.h"
33 #include "playlists/PlayList.h"
34 #include "playlists/PlayListFactory.h"
35 #include "settings/MediaSourceSettings.h"
36 #include "settings/Settings.h"
37 #include "settings/SettingsComponent.h"
38 #include "utils/SortUtils.h"
39 #include "utils/StringUtils.h"
40 #include "utils/URIUtils.h"
41 #include "utils/Variant.h"
42 #include "utils/XTimeUtils.h"
43 #include "utils/log.h"
44 #include "video/VideoFileItemClassify.h"
45 #include "view/GUIViewState.h"
47 #define CONTROL_BTNSORTASC 4
48 #define CONTROL_LABELFILES 12
50 using namespace XFILE;
51 using namespace KODI::MESSAGING;
52 using namespace KODI;
54 using namespace std::chrono_literals;
56 #define CONTROL_BTNSLIDESHOW 6
57 #define CONTROL_BTNSLIDESHOW_RECURSIVE 7
58 #define CONTROL_SHUFFLE 9
60 CGUIWindowPictures::CGUIWindowPictures(void)
61 : CGUIMediaWindow(WINDOW_PICTURES, "MyPics.xml")
63 m_thumbLoader.SetObserver(this);
64 m_slideShowStarted = false;
65 m_dlgProgress = NULL;
68 void CGUIWindowPictures::OnInitWindow()
70 CGUIMediaWindow::OnInitWindow();
71 if (m_slideShowStarted)
73 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
74 std::string path;
75 if (slideShow.GetCurrentSlide())
76 path = URIUtils::GetDirectory(slideShow.GetCurrentSlide()->GetPath());
77 if (m_vecItems->IsPath(path))
79 if (slideShow.GetCurrentSlide())
80 m_viewControl.SetSelectedItem(slideShow.GetCurrentSlide()->GetPath());
81 SaveSelectedItemInHistory();
83 m_slideShowStarted = false;
87 CGUIWindowPictures::~CGUIWindowPictures(void) = default;
89 bool CGUIWindowPictures::OnMessage(CGUIMessage& message)
91 switch ( message.GetMessage() )
93 case GUI_MSG_WINDOW_DEINIT:
95 if (m_thumbLoader.IsLoading())
96 m_thumbLoader.StopThread();
99 break;
101 case GUI_MSG_WINDOW_INIT:
103 // is this the first time accessing this window?
104 if (m_vecItems->GetPath() == "?" && message.GetStringParam().empty())
105 message.SetStringParam(CMediaSourceSettings::GetInstance().GetDefaultSource("pictures"));
107 m_dlgProgress = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogProgress>(WINDOW_DIALOG_PROGRESS);
109 break;
111 case GUI_MSG_CLICKED:
113 int iControl = message.GetSenderId();
114 if (iControl == CONTROL_BTNSLIDESHOW) // Slide Show
116 OnSlideShow();
118 else if (iControl == CONTROL_BTNSLIDESHOW_RECURSIVE) // Recursive Slide Show
120 OnSlideShowRecursive();
122 else if (iControl == CONTROL_SHUFFLE)
124 const std::shared_ptr<CSettings> settings = CServiceBroker::GetSettingsComponent()->GetSettings();
125 settings->ToggleBool(CSettings::SETTING_SLIDESHOW_SHUFFLE);
126 settings->Save();
128 else if (m_viewControl.HasControl(iControl)) // list/thumb control
130 int iItem = m_viewControl.GetSelectedItem();
131 int iAction = message.GetParam1();
133 // iItem is checked for validity inside these routines
134 if (iAction == ACTION_DELETE_ITEM)
136 // is delete allowed?
137 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION))
138 OnDeleteItem(iItem);
139 else
140 return false;
142 else if (iAction == ACTION_PLAYER_PLAY)
144 ShowPicture(iItem, true);
145 return true;
147 else if (iAction == ACTION_SHOW_INFO)
149 OnItemInfo(iItem);
150 return true;
154 break;
156 return CGUIMediaWindow::OnMessage(message);
159 void CGUIWindowPictures::UpdateButtons()
161 CGUIMediaWindow::UpdateButtons();
163 // Update the shuffle button
164 SET_CONTROL_SELECTED(GetID(), CONTROL_SHUFFLE, CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_SLIDESHOW_SHUFFLE));
166 // check we can slideshow or recursive slideshow
167 int nFolders = m_vecItems->GetFolderCount();
168 if (nFolders == m_vecItems->Size() ||
169 m_vecItems->GetPath() == "addons://sources/image/")
171 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW);
173 else
175 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW);
177 if (m_guiState.get() && !m_guiState->HideParentDirItems())
178 nFolders--;
179 if (m_vecItems->Size() == 0 || nFolders == 0 ||
180 m_vecItems->GetPath() == "addons://sources/image/")
182 CONTROL_DISABLE(CONTROL_BTNSLIDESHOW_RECURSIVE);
184 else
186 CONTROL_ENABLE(CONTROL_BTNSLIDESHOW_RECURSIVE);
190 void CGUIWindowPictures::OnPrepareFileItems(CFileItemList& items)
192 CGUIMediaWindow::OnPrepareFileItems(items);
194 for (int i=0;i<items.Size();++i )
195 if (StringUtils::EqualsNoCase(items[i]->GetLabel(), "folder.jpg"))
196 items.Remove(i);
198 if (items.GetFolderCount() == items.Size() || !CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_USETAGS))
199 return;
201 // Start the music info loader thread
202 CPictureInfoLoader loader;
203 loader.SetProgressCallback(m_dlgProgress);
204 loader.Load(items);
206 bool bShowProgress = !CServiceBroker::GetGUI()->GetWindowManager().HasModalDialog(true);
207 bool bProgressVisible = false;
209 auto start = std::chrono::steady_clock::now();
211 while (loader.IsLoading() && m_dlgProgress && !m_dlgProgress->IsCanceled())
213 if (bShowProgress)
214 { // Do we have to init a progress dialog?
215 auto end = std::chrono::steady_clock::now();
216 auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
218 if (!bProgressVisible && duration.count() > 1500 && m_dlgProgress)
219 { // tag loading takes more then 1.5 secs, show a progress dialog
220 CURL url(items.GetPath());
222 m_dlgProgress->SetHeading(CVariant{189});
223 m_dlgProgress->SetLine(0, CVariant{505});
224 m_dlgProgress->SetLine(1, CVariant{""});
225 m_dlgProgress->SetLine(2, CVariant{url.GetWithoutUserDetails()});
226 m_dlgProgress->Open();
227 m_dlgProgress->ShowProgressBar(true);
228 bProgressVisible = true;
231 if (bProgressVisible && m_dlgProgress)
232 { // keep GUI alive
233 m_dlgProgress->Progress();
235 } // if (bShowProgress)
236 KODI::TIME::Sleep(1ms);
237 } // while (loader.IsLoading())
239 if (bProgressVisible && m_dlgProgress)
240 m_dlgProgress->Close();
243 bool CGUIWindowPictures::Update(const std::string &strDirectory, bool updateFilterPath /* = true */)
245 if (m_thumbLoader.IsLoading())
246 m_thumbLoader.StopThread();
248 if (!CGUIMediaWindow::Update(strDirectory, updateFilterPath))
249 return false;
251 m_vecItems->SetArt("thumb", "");
252 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_GENERATETHUMBS))
253 m_thumbLoader.Load(*m_vecItems);
255 CPictureThumbLoader thumbLoader;
256 std::string thumb = thumbLoader.GetCachedImage(*m_vecItems, "thumb");
257 m_vecItems->SetArt("thumb", thumb);
259 return true;
262 bool CGUIWindowPictures::OnClick(int iItem, const std::string &player)
264 if ( iItem < 0 || iItem >= m_vecItems->Size() ) return true;
265 CFileItemPtr pItem = m_vecItems->Get(iItem);
267 if (pItem->IsCBZ() || pItem->IsCBR())
269 CURL pathToUrl;
270 if (pItem->IsCBZ())
271 pathToUrl = URIUtils::CreateArchivePath("zip", pItem->GetURL(), "");
272 else
273 pathToUrl = URIUtils::CreateArchivePath("rar", pItem->GetURL(), "");
275 OnShowPictureRecursive(pathToUrl.Get());
276 return true;
278 else if (CGUIMediaWindow::OnClick(iItem, player))
279 return true;
281 return false;
284 bool CGUIWindowPictures::GetDirectory(const std::string &strDirectory, CFileItemList& items)
286 if (!CGUIMediaWindow::GetDirectory(strDirectory, items))
287 return false;
289 std::string label;
290 if (items.GetLabel().empty() && m_rootDir.IsSource(items.GetPath(), CMediaSourceSettings::GetInstance().GetSources("pictures"), &label))
291 items.SetLabel(label);
293 if (items.GetContent().empty() && !items.IsVirtualDirectoryRoot() && !items.IsPlugin())
294 items.SetContent("images");
295 return true;
298 bool CGUIWindowPictures::OnPlayMedia(int iItem, const std::string &player)
300 if (VIDEO::IsVideo(*m_vecItems->Get(iItem)))
301 return CGUIMediaWindow::OnPlayMedia(iItem);
303 return ShowPicture(iItem, false);
306 bool CGUIWindowPictures::ShowPicture(int iItem, bool startSlideShow)
308 if ( iItem < 0 || iItem >= m_vecItems->Size() ) return false;
309 CFileItemPtr pItem = m_vecItems->Get(iItem);
310 std::string strPicture = pItem->GetPath();
312 #ifdef HAS_OPTICAL_DRIVE
313 if (pItem->IsDVD())
314 return MEDIA_DETECT::CAutorun::PlayDiscAskResume(m_vecItems->Get(iItem)->GetPath());
315 #endif
317 if (pItem->m_bIsShareOrDrive)
318 return false;
320 //! @todo this should be reactive, based on a given event app player should stop the playback
321 const auto& components = CServiceBroker::GetAppComponents();
322 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
323 if (appPlayer->IsPlayingVideo())
324 g_application.StopPlaying();
326 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
327 slideShow.Reset();
328 bool bShowVideos = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_PICTURES_SHOWVIDEOS);
329 for (const auto& pItem : *m_vecItems)
331 if (!pItem->m_bIsFolder &&
332 !(URIUtils::IsRAR(pItem->GetPath()) || URIUtils::IsZIP(pItem->GetPath())) &&
333 (pItem->IsPicture() || (bShowVideos && VIDEO::IsVideo(*pItem))))
335 slideShow.Add(pItem.get());
339 if (slideShow.NumSlides() == 0)
340 return false;
342 slideShow.Select(strPicture);
344 if (startSlideShow)
345 slideShow.StartSlideShow();
346 else
348 slideShow.PlayPicture();
351 //! @todo this should trigger some event that should led the window manager to activate another window
352 // look into using OnPlay announce!
353 m_slideShowStarted = true;
354 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
356 return true;
359 void CGUIWindowPictures::OnShowPictureRecursive(const std::string& strPath)
361 // stop any video
362 //! @todo this should be reactive, based on a given event app player should stop the playback
363 const auto& components = CServiceBroker::GetAppComponents();
364 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
365 if (appPlayer->IsPlayingVideo())
366 g_application.StopPlaying();
368 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
369 SortDescription sorting = m_guiState->GetSortMethod();
370 slideShow.AddFromPath(strPath, true, sorting.sortBy, sorting.sortOrder, sorting.sortAttributes);
371 //! @todo window manager should react to a given event and start the window itself!
372 if (slideShow.NumSlides())
374 m_slideShowStarted = true;
375 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
379 void CGUIWindowPictures::OnSlideShowRecursive(const std::string &strPicture)
382 std::string strExtensions;
383 CFileItemList items;
384 CGUIViewState* viewState=CGUIViewState::GetViewState(GetID(), items);
385 if (viewState)
387 strExtensions = viewState->GetExtensions();
388 delete viewState;
390 m_slideShowStarted = true;
392 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
393 SortDescription sorting = m_guiState->GetSortMethod();
394 slideShow.RunSlideShow(strPicture, true,
395 CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(
396 CSettings::SETTING_SLIDESHOW_SHUFFLE),
397 false, "", true, sorting.sortBy, sorting.sortOrder,
398 sorting.sortAttributes, strExtensions);
401 void CGUIWindowPictures::OnSlideShowRecursive()
403 OnSlideShowRecursive(m_vecItems->GetPath());
406 void CGUIWindowPictures::OnSlideShow()
408 OnSlideShow(m_vecItems->GetPath());
411 void CGUIWindowPictures::OnSlideShow(const std::string &strPicture)
413 std::string strExtensions;
414 CFileItemList items;
415 CGUIViewState* viewState = CGUIViewState::GetViewState(GetID(), items);
416 if (viewState)
418 strExtensions = viewState->GetExtensions();
419 delete viewState;
421 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
422 m_slideShowStarted = true;
423 SortDescription sorting = m_guiState->GetSortMethod();
424 slideShow.RunSlideShow(strPicture, false, false, false, "", true, sorting.sortBy,
425 sorting.sortOrder, sorting.sortAttributes, strExtensions);
428 void CGUIWindowPictures::OnRegenerateThumbs()
430 if (m_thumbLoader.IsLoading()) return;
431 m_thumbLoader.SetRegenerateThumbs(true);
432 m_thumbLoader.Load(*m_vecItems);
435 void CGUIWindowPictures::GetContextButtons(int itemNumber, CContextButtons &buttons)
437 CFileItemPtr item;
438 if (itemNumber >= 0 && itemNumber < m_vecItems->Size())
439 item = m_vecItems->Get(itemNumber);
441 if (item)
443 if ( m_vecItems->IsVirtualDirectoryRoot() || m_vecItems->GetPath() == "sources://pictures/" )
445 CGUIDialogContextMenu::GetContextButtons("pictures", item, buttons);
447 else
449 if (item)
451 if (!(item->m_bIsFolder || item->IsZIP() || item->IsRAR() || item->IsCBZ() || item->IsCBR() || item->IsScript()))
453 if (item->IsPicture())
454 buttons.Add(CONTEXT_BUTTON_INFO, 13406); // picture info
455 buttons.Add(CONTEXT_BUTTON_VIEW_SLIDESHOW, item->m_bIsFolder ? 13317 : 13422); // View Slideshow
457 if (item->m_bIsFolder)
458 buttons.Add(CONTEXT_BUTTON_RECURSIVE_SLIDESHOW, 13318); // Recursive Slideshow
460 if (!m_thumbLoader.IsLoading())
461 buttons.Add(CONTEXT_BUTTON_REFRESH_THUMBS, 13315); // Create Thumbnails
462 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_FILELISTS_ALLOWFILEDELETION) && !item->IsReadOnly())
464 buttons.Add(CONTEXT_BUTTON_DELETE, 117);
465 buttons.Add(CONTEXT_BUTTON_RENAME, 118);
469 if (!item->IsPlugin() && !item->IsScript() && !m_vecItems->IsPlugin())
470 buttons.Add(CONTEXT_BUTTON_SWITCH_MEDIA, 523);
473 CGUIMediaWindow::GetContextButtons(itemNumber, buttons);
476 bool CGUIWindowPictures::OnContextButton(int itemNumber, CONTEXT_BUTTON button)
478 CFileItemPtr item = (itemNumber >= 0 && itemNumber < m_vecItems->Size()) ? m_vecItems->Get(itemNumber) : CFileItemPtr();
479 if (CGUIDialogContextMenu::OnContextButton("pictures", item, button))
481 Update("");
482 return true;
484 switch (button)
486 case CONTEXT_BUTTON_VIEW_SLIDESHOW:
487 if (item && item->m_bIsFolder)
488 OnSlideShow(item->GetPath());
489 else
490 ShowPicture(itemNumber, true);
491 return true;
492 case CONTEXT_BUTTON_RECURSIVE_SLIDESHOW:
493 if (item)
494 OnSlideShowRecursive(item->GetPath());
495 return true;
496 case CONTEXT_BUTTON_INFO:
497 OnItemInfo(itemNumber);
498 return true;
499 case CONTEXT_BUTTON_REFRESH_THUMBS:
500 OnRegenerateThumbs();
501 return true;
502 case CONTEXT_BUTTON_DELETE:
503 OnDeleteItem(itemNumber);
504 return true;
505 case CONTEXT_BUTTON_RENAME:
506 OnRenameItem(itemNumber);
507 return true;
508 case CONTEXT_BUTTON_SWITCH_MEDIA:
509 CGUIDialogContextMenu::SwitchMedia("pictures", m_vecItems->GetPath());
510 return true;
511 default:
512 break;
514 return CGUIMediaWindow::OnContextButton(itemNumber, button);
517 bool CGUIWindowPictures::OnAddMediaSource()
519 return CGUIDialogMediaSource::ShowAndAddMediaSource("pictures");
522 void CGUIWindowPictures::OnItemLoaded(CFileItem *pItem)
524 CPictureThumbLoader::ProcessFoldersAndArchives(pItem);
527 void CGUIWindowPictures::LoadPlayList(const std::string& strPlayList)
529 CLog::Log(LOGDEBUG,
530 "CGUIWindowPictures::LoadPlayList()... converting playlist into slideshow: {}",
531 strPlayList);
532 std::unique_ptr<PLAYLIST::CPlayList> pPlayList(PLAYLIST::CPlayListFactory::Create(strPlayList));
533 if (nullptr != pPlayList)
535 if (!pPlayList->Load(strPlayList))
537 HELPERS::ShowOKDialogText(CVariant{6}, CVariant{477});
538 return ; //hmmm unable to load playlist?
542 PLAYLIST::CPlayList playlist = *pPlayList;
543 if (playlist.size() > 0)
545 //! @todo this should be reactive, based on a given event app player should stop the playback
546 const auto& components = CServiceBroker::GetAppComponents();
547 const auto appPlayer = components.GetComponent<CApplicationPlayer>();
548 if (appPlayer->IsPlayingVideo())
549 g_application.StopPlaying();
551 CSlideShowDelegator& slideShow = CServiceBroker::GetSlideShowDelegator();
552 // convert playlist items into slideshow items
553 slideShow.Reset();
554 for (int i = 0; i < playlist.size(); ++i)
556 CFileItemPtr pItem = playlist[i];
557 //CLog::Log(LOGDEBUG,"-- playlist item: {}", pItem->GetPath());
558 if (pItem->IsPicture() && !(pItem->IsZIP() || pItem->IsRAR() || pItem->IsCBZ() || pItem->IsCBR()))
560 slideShow.Add(pItem.get());
564 // start slideshow if there are items
565 slideShow.StartSlideShow();
566 //! @todo this should be reactive based on a triggered event the window manager is the only component
567 // that should be responsible to activate the slideshow window
568 if (slideShow.NumSlides())
569 CServiceBroker::GetGUI()->GetWindowManager().ActivateWindow(WINDOW_SLIDESHOW);
573 void CGUIWindowPictures::OnItemInfo(int itemNumber)
575 CFileItemPtr item = m_vecItems->Get(itemNumber);
576 if (!item)
577 return;
578 if (!m_vecItems->IsPlugin() && (item->IsPlugin() || item->IsScript()))
580 CGUIDialogAddonInfo::ShowForItem(item);
581 return;
583 if (item->m_bIsFolder || item->IsZIP() || item->IsRAR() || item->IsCBZ() || item->IsCBR() || !item->IsPicture())
584 return;
585 CGUIDialogPictureInfo *pictureInfo = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogPictureInfo>(WINDOW_DIALOG_PICTURE_INFO);
586 if (pictureInfo)
588 pictureInfo->SetPicture(item.get());
589 pictureInfo->Open();
593 std::string CGUIWindowPictures::GetStartFolder(const std::string &dir)
595 if (StringUtils::EqualsNoCase(dir, "plugins") ||
596 StringUtils::EqualsNoCase(dir, "addons"))
597 return "addons://sources/image/";
599 SetupShares();
600 VECSOURCES shares;
601 m_rootDir.GetSources(shares);
602 bool bIsSourceName = false;
603 int iIndex = CUtil::GetMatchingSource(dir, shares, bIsSourceName);
604 if (iIndex > -1)
606 if (iIndex < static_cast<int>(shares.size()) && shares[iIndex].m_iHasLock == LOCK_STATE_LOCKED)
608 CFileItem item(shares[iIndex]);
609 if (!g_passwordManager.IsItemUnlocked(&item,"pictures"))
610 return "";
612 if (bIsSourceName)
613 return shares[iIndex].strPath;
614 return dir;
616 return CGUIMediaWindow::GetStartFolder(dir);