MythTV  master
themechooser.cpp
Go to the documentation of this file.
1 
2 // Theme Chooser headers
3 #include "themechooser.h"
4 
5 // C++ headers
6 #include <chrono>
7 
8 // Qt headers
9 #include <QCoreApplication>
10 #include <QRegularExpression>
11 #include <QRunnable>
12 
13 // MythTV headers
14 #include "mythcorecontext.h"
15 #include "mythcoreutil.h"
16 #include "mthreadpool.h"
17 #include "remotefile.h"
18 #include "mythdownloadmanager.h"
19 #include "programtypes.h"
20 #include "mythsystemevent.h"
21 #include "mythdate.h"
22 #include "mythversion.h"
23 #include "mythlogging.h"
24 #include "storagegroup.h"
25 
26 // LibMythUI headers
27 #include "mythdialogbox.h"
28 #include "mythmainwindow.h"
29 #include "mythscreenstack.h"
30 #include "mythuibuttonlist.h"
31 #include "mythuigroup.h"
32 #include "mythuihelper.h"
33 #include "mythuiimage.h"
34 #include "mythuiprogressbar.h"
35 #include "mythuistatetype.h"
36 #include "mythuitext.h"
37 
38 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
39 #define capturedView capturedRef
40 #endif
41 
42 #define LOC QString("ThemeChooser: ")
43 #define LOC_WARN QString("ThemeChooser, Warning: ")
44 #define LOC_ERR QString("ThemeChooser, Error: ")
45 
49 class ThemeExtractThread : public QRunnable
50 {
51  public:
53  QString srcFile, QString destDir) :
54  m_parent(parent),
55  m_srcFile(std::move(srcFile)),
56  m_destDir(std::move(destDir)) {}
57 
58  void run() override // QRunnable
59  {
61 
62  auto *me = new MythEvent("THEME_INSTALLED", QStringList(m_srcFile));
63  QCoreApplication::postEvent(m_parent, me);
64  }
65 
66  private:
67  ThemeChooser *m_parent {nullptr};
68  QString m_srcFile;
69  QString m_destDir;
70 };
71 
72 
78  const QString &name) :
79  MythScreenType(parent, name)
80 {
82 
83  StorageGroup sgroup("Themes", gCoreContext->GetHostName());
84  m_userThemeDir = sgroup.GetFirstDir(true);
85 }
86 
88 {
90 }
91 
92 static bool sortThemeNames(const QFileInfo &s1, const QFileInfo &s2)
93 {
94  return s1.fileName().toLower() < s2.fileName().toLower();
95 }
96 
97 
99 {
100  // Load the theme for this screen
101  if (!LoadWindowFromXML("settings-ui.xml", "themechooser", this))
102  return false;
103 
104  bool err = false;
105  UIUtilE::Assign(this, m_themes, "themes", &err);
106 
107  UIUtilW::Assign(this, m_preview, "preview");
108  UIUtilW::Assign(this, m_fullPreviewStateType, "fullpreviewstate");
109 
111  {
112  MythUIGroup *state =
113  dynamic_cast<MythUIGroup*>
114  (m_fullPreviewStateType->GetChild("fullscreen"));
115  if (state)
116  {
118  dynamic_cast<MythUIText*>(state->GetChild("fullscreenname"));
120  dynamic_cast<MythUIImage*>(state->GetChild("fullscreenpreview"));
121  }
122  }
123 
124  if (err)
125  {
126  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load screen 'themechooser'");
127  return false;
128  }
129 
131  this, qOverload<MythUIButtonListItem*>(&ThemeChooser::saveAndReload));
134 
135  BuildFocusList();
136 
138 
139  return true;
140 }
141 
143 {
144  SetBusyPopupMessage(tr("Loading Installed Themes"));
145 
146  QStringList themesSeen;
147  QDir themes(m_userThemeDir);
148  themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
149  themes.setSorting(QDir::Name | QDir::IgnoreCase);
150 
151  m_infoList = themes.entryInfoList();
152 
153  for (const auto & theme : qAsConst(m_infoList))
154  {
155  if (loadThemeInfo(theme))
156  {
157  themesSeen << theme.fileName();
158  m_themeStatuses[theme.fileName()] = "default";
159  }
160  }
161 
162  themes.setPath(GetThemesParentDir());
163  QFileInfoList sharedThemes = themes.entryInfoList();
164  for (const auto & sharedTheme : qAsConst(sharedThemes))
165  {
166  if ((!themesSeen.contains(sharedTheme.fileName())) &&
167  (loadThemeInfo(sharedTheme)))
168  {
169  m_infoList << sharedTheme;
170  themesSeen << sharedTheme.fileName();
171  m_themeStatuses[sharedTheme.fileName()] = "default";
172  }
173  }
174 
175  // MYTH_SOURCE_VERSION - examples v29-pre-574-g92517f5, v29-Pre, v29.1-21-ge26a33c
176  QString MythVersion(GetMythSourceVersion());
177  static const QRegularExpression trunkver
178  { "\\Av[0-9]+-pre.*\\z", QRegularExpression::CaseInsensitiveOption };
179  static const QRegularExpression validver {
180  "\\Av[0-9]+.*\\z", QRegularExpression::CaseInsensitiveOption };
181 
182  auto match = validver.match(MythVersion);
183  if (!match.hasMatch())
184  {
185  LOG(VB_GENERAL, LOG_ERR, QString("Invalid MythTV version %1, will use themes from trunk").arg(MythVersion));
186  MythVersion = "trunk";
187  }
188  match = trunkver.match(MythVersion);
189  if (match.hasMatch())
190  MythVersion = "trunk";
191 
192  if (MythVersion == "trunk")
193  {
194  LoadVersion(MythVersion, themesSeen, true);
195  LOG(VB_GUI, LOG_INFO, QString("Loading themes for %1").arg(MythVersion));
196  }
197  else
198  {
199 
200  MythVersion = MYTH_BINARY_VERSION; // Example: 29.20161017-1
201  // Remove the date part and the rest, eg 29.20161017-1 -> 29
202  MythVersion.remove(QRegularExpression("\\.[0-9]{8,}.*"));
203  LOG(VB_GUI, LOG_INFO, QString("Loading themes for %1").arg(MythVersion));
204  LoadVersion(MythVersion, themesSeen, true);
205 
206  // If a version of the theme for this tag exists, use it...
207  // MYTH_SOURCE_VERSION - examples v29-pre-574-g92517f5, v29-Pre, v29.1-21-ge26a33c
208  static const QRegularExpression subexp
209  { "v[0-9]+\\.([0-9]+)-*", QRegularExpression::CaseInsensitiveOption };
210  // This captures the subversion, i.e. the number after a dot
211  match = subexp.match(GetMythSourceVersion());
212  if (match.hasMatch())
213  {
214  QString subversion;
215  for (int idx = match.capturedView(1).toInt(); idx > 0; --idx)
216  {
217  subversion = MythVersion + "." + QString::number(idx);
218  LOG(VB_GUI, LOG_INFO, QString("Loading themes for %1").arg(subversion));
219  LoadVersion(subversion, themesSeen, false);
220  }
221  }
222  }
223 
224  ResetBusyPopup();
225 
226  std::sort(m_infoList.begin(), m_infoList.end(), sortThemeNames);
227 }
228 
229 void ThemeChooser::LoadVersion(const QString &version,
230  QStringList &themesSeen, bool alert_user)
231 {
232  QString remoteThemesFile = GetConfDir();
233  remoteThemesFile.append("/tmp/themes.zip");
234  QString themeSite = QString("%1/%2")
235  .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
236  "http://themes.mythtv.org/themes/repository"), version);
237  QString destdir = GetCacheDir().append("/themechooser/");
238  QString versiondir = QString("%1/%2").arg(destdir, version);
239  QDir remoteThemesDir(versiondir);
240 
241  int downloadFailures =
242  gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
243  if (QFile::exists(remoteThemesFile))
244  {
245  QFileInfo finfo(remoteThemesFile);
246  if (finfo.lastModified().toUTC() <
247  MythDate::current().addSecs(-600))
248  {
249  LOG(VB_GUI, LOG_INFO, LOC +
250  QString("%1 is over 10 minutes old, forcing "
251  "remote theme list download").arg(remoteThemesFile));
253  }
254 
255  if (!remoteThemesDir.exists())
257  }
258  else if (downloadFailures < 2) // (and themes.zip does not exist)
259  {
260  LOG(VB_GUI, LOG_INFO, LOC +
261  QString("%1 does not exist, forcing remote theme "
262  "list download").arg(remoteThemesFile));
264  }
265 
267  {
268  QFile test(remoteThemesFile);
269  if (test.open(QIODevice::WriteOnly))
270  test.remove();
271  else
272  {
273  ShowOkPopup(tr("Unable to create '%1'").arg(remoteThemesFile));
274  return;
275  }
276 
277  SetBusyPopupMessage(tr("Refreshing Downloadable Themes Information"));
278 
279  QString url = themeSite;
280  url.append("/themes.zip");
281  if (!removeThemeDir(versiondir))
282  ShowOkPopup(tr("Unable to remove '%1'").arg(versiondir));
283  QDir dir;
284  if (!dir.mkpath(destdir))
285  ShowOkPopup(tr("Unable to create '%1'").arg(destdir));
286  bool result = GetMythDownloadManager()->download(url, remoteThemesFile, true);
287 
288  LOG(VB_GUI, LOG_INFO, LOC +
289  QString("Downloading '%1' to '%2'").arg(url, remoteThemesFile));
290 
291  SetBusyPopupMessage(tr("Extracting Downloadable Themes Information"));
292 
293  if (!result || !extractZIP(remoteThemesFile, destdir))
294  {
295  QFile::remove(remoteThemesFile);
296 
297  downloadFailures++;
298  gCoreContext->SaveSetting("ThemeInfoDownloadFailures",
299  downloadFailures);
300 
301  if (!result)
302  {
303  LOG(VB_GUI, LOG_ERR, LOC +
304  QString("Failed to download '%1'").arg(url));
305  if (alert_user)
306  ShowOkPopup(tr("Failed to download '%1'").arg(url));
307  }
308  else
309  {
310  LOG(VB_GUI, LOG_ERR, LOC +
311  QString("Failed to unzip '%1' to '%2'")
312  .arg(remoteThemesFile, destdir));
313  if (alert_user)
314  ShowOkPopup(tr("Failed to unzip '%1' to '%2'")
315  .arg(remoteThemesFile, destdir));
316  }
317  }
318  else
319  {
320  LOG(VB_GUI, LOG_INFO, LOC +
321  QString("Unzipped '%1' to '%2'")
322  .arg(remoteThemesFile, destdir));
323  }
324  }
325 
326  QDir themes;
327  themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
328  themes.setSorting(QDir::Name | QDir::IgnoreCase);
329 
330  if ((QFile::exists(remoteThemesFile)) &&
331  (remoteThemesDir.exists()))
332  {
333  SetBusyPopupMessage(tr("Loading Downloadable Themes"));
334 
335  LOG(VB_GUI, LOG_INFO, LOC +
336  QString("%1 and %2 exist, using cached remote themes list")
337  .arg(remoteThemesFile, remoteThemesDir.absolutePath()));
338 
339  QString themesPath = remoteThemesDir.absolutePath();
340  themes.setPath(themesPath);
341 
342  QFileInfoList downloadableThemes = themes.entryInfoList();
343  for (const auto & dtheme : qAsConst(downloadableThemes))
344  {
345  QString dirName = dtheme.fileName();
346  QString themeName = dirName;
347  QString remoteDir = themeSite;
348  remoteDir.append("/").append(dirName);
349  QString localDir = themes.absolutePath();
350  localDir.append("/").append(dirName);
351 
352  ThemeInfo remoteTheme(dtheme.absoluteFilePath());
353 
354  if (themesSeen.contains(dirName))
355  {
356  ThemeInfo *localTheme = m_themeNameInfos[dirName];
357 
358  themeName = remoteTheme.GetName();
359 
360  int rmtMaj = remoteTheme.GetMajorVersion();
361  int rmtMin = remoteTheme.GetMinorVersion();
362  int locMaj = localTheme->GetMajorVersion();
363  int locMin = localTheme->GetMinorVersion();
364 
365  if ((rmtMaj > locMaj) ||
366  ((rmtMaj == locMaj) &&
367  (rmtMin > locMin)))
368  {
369  if (loadThemeInfo(dtheme))
370  {
371  LOG(VB_GUI, LOG_DEBUG, LOC +
372  QString("'%1' old version %2.%3, new version %4.%5")
373  .arg(themeName).arg(locMaj).arg(locMin)
374  .arg(rmtMaj).arg(rmtMin));
375 
376  m_infoList << dtheme;
377  m_themeStatuses[themeName] = "updateavailable";
378 
379  QFileInfo finfo(remoteTheme.GetPreviewPath());
381  remoteDir.append("/").append(finfo.fileName()),
382  localDir.append("/").append(finfo.fileName()),
383  nullptr);
384  }
385  }
386  else if ((rmtMaj == locMaj) &&
387  (rmtMin == locMin))
388  {
389  LOG(VB_GUI, LOG_DEBUG, LOC +
390  QString("'%1' up to date (%2.%3)")
391  .arg(themeName).arg(locMaj).arg(locMin));
392 
393  m_themeStatuses[themeName] = "uptodate";
394  }
395  }
396  else
397  {
398  LOG(VB_GUI, LOG_DEBUG, LOC +
399  QString("'%1' (%2.%3) available")
400  .arg(themeName)
401  .arg(remoteTheme.GetMajorVersion())
402  .arg(remoteTheme.GetMinorVersion()));
403 
404  ThemeInfo *tmpTheme = loadThemeInfo(dtheme);
405  if (tmpTheme)
406  {
407  themeName = tmpTheme->GetName();
408  themesSeen << dirName;
409  m_infoList << dtheme;
410  m_themeStatuses[themeName] = "updateavailable";
411 
412  QFileInfo finfo(tmpTheme->GetPreviewPath());
414  remoteDir.append("/").append(finfo.fileName()),
415  localDir.append("/").append(finfo.fileName()),
416  nullptr);
417  }
418  }
419  }
420  }
421 
422 }
423 
425 {
426  QString curTheme = gCoreContext->GetSetting("Theme");
427  ThemeInfo *themeinfo = nullptr;
428  ThemeInfo *curThemeInfo = nullptr;
429  MythUIButtonListItem *item = nullptr;
430 
431  m_themes->Reset();
432  for (const auto & theme : qAsConst(m_infoList))
433  {
434  if (!m_themeFileNameInfos.contains(theme.filePath()))
435  continue;
436 
437  themeinfo = m_themeFileNameInfos[theme.filePath()];
438  if (!themeinfo)
439  continue;
440 
441  QString buttonText = QString("%1 %2.%3")
442  .arg(themeinfo->GetName())
443  .arg(themeinfo->GetMajorVersion())
444  .arg(themeinfo->GetMinorVersion());
445 
446  item = new MythUIButtonListItem(m_themes, buttonText);
447  if (item)
448  {
449  if (themeinfo->GetDownloadURL().isEmpty())
450  item->DisplayState("local", "themelocation");
451  else
452  item->DisplayState("remote", "themelocation");
453 
454  item->DisplayState(themeinfo->GetAspect(), "aspectstate");
455 
456  item->DisplayState(m_themeStatuses[themeinfo->GetName()],
457  "themestatus");
458  InfoMap infomap;
459  themeinfo->ToMap(infomap);
460  item->SetTextFromMap(infomap);
461  item->SetData(QVariant::fromValue(themeinfo));
462 
463  QString thumbnail = themeinfo->GetPreviewPath();
464  // Downloadable themeinfos have thumbnail copies of their preview images
465  if (!themeinfo->GetDownloadURL().isEmpty())
466  thumbnail = thumbnail.append(".thumb.jpg");
467  item->SetImage(thumbnail);
468 
469  if (curTheme == themeinfo->GetDirectoryName())
470  curThemeInfo = themeinfo;
471  }
472  }
473 
475 
476  if (curThemeInfo)
477  m_themes->SetValueByData(QVariant::fromValue(curThemeInfo));
478 
480  if (current)
482 
483  QString testFile = m_userThemeDir + "/.test";
484  QFile test(testFile);
485  if (test.open(QIODevice::WriteOnly))
486  test.remove();
487  else
488  {
489  ShowOkPopup(tr("Error creating test file, %1 themes directory is "
490  "not writable.").arg(m_userThemeDir));
491  }
492 }
493 
494 ThemeInfo *ThemeChooser::loadThemeInfo(const QFileInfo &theme)
495 {
496  if (theme.fileName() == "default" || theme.fileName() == "default-wide")
497  return nullptr;
498 
499  ThemeInfo *themeinfo = nullptr;
500  if (theme.exists()) // local directory vs http:// or remote URL
501  themeinfo = new ThemeInfo(theme.absoluteFilePath());
502  else
503  themeinfo = new ThemeInfo(theme.filePath());
504 
505  if (!themeinfo)
506  return nullptr;
507 
508  if (themeinfo->GetName().isEmpty() || ((themeinfo->GetType() & THEME_UI) == 0))
509  {
510  delete themeinfo;
511  return nullptr;
512  }
513 
514  m_themeFileNameInfos[theme.filePath()] = themeinfo;
515  m_themeNameInfos[theme.fileName()] = themeinfo;
516 
517  return themeinfo;
518 }
519 
521 {
522  if (m_popupMenu)
523  return;
524 
525  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
526  QString label = tr("Theme Chooser Menu");
527 
528  m_popupMenu =
529  new MythDialogBox(label, popupStack, "themechoosermenupopup");
530 
532 
533  if (m_popupMenu->Create())
534  popupStack->AddScreen(m_popupMenu);
535  else
536  {
537  delete m_popupMenu;
538  m_popupMenu = nullptr;
539  return;
540  }
541 
542  m_popupMenu->SetReturnEvent(this, "popupmenu");
543 
545  {
547  {
548  m_popupMenu->AddButton(tr("Hide Fullscreen Preview"),
550  }
551  else
552  {
553  m_popupMenu->AddButton(tr("Show Fullscreen Preview"),
555  }
556  }
557 
558  m_popupMenu->AddButton(tr("Refresh Downloadable Themes"),
560 
562  if (current)
563  {
564  auto *info = current->GetData().value<ThemeInfo *>();
565 
566  if (info)
567  {
568  m_popupMenu->AddButton(tr("Select Theme"),
569  qOverload<>(&ThemeChooser::saveAndReload));
570 
571  if (info->GetPreviewPath().startsWith(m_userThemeDir))
572  m_popupMenu->AddButton(tr("Delete Theme"),
574  }
575  }
576 
577  if (gCoreContext->GetBoolSetting("ThemeUpdateNofications", true))
578  {
579  m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
581  }
582  else
583  {
584  m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
586  }
587 }
588 
589 void ThemeChooser::popupClosed(const QString& which, int result)
590 {
591  (void)which;
592  (void)result;
593 
594  m_popupMenu = nullptr;
595 }
596 
597 bool ThemeChooser::keyPressEvent(QKeyEvent *event)
598 {
599  if (GetFocusWidget()->keyPressEvent(event))
600  return true;
601 
602  QStringList actions;
603  bool handled = GetMythMainWindow()->TranslateKeyPress("Theme Chooser", event, actions);
604 
605  for (int i = 0; i < actions.size() && !handled; ++i)
606  {
607  QString action = actions[i];
608  handled = true;
609 
610  if (action == "MENU")
611  showPopupMenu();
612  else if (action == "DELETE")
613  removeTheme();
614  else if ((action == "ESCAPE") &&
616  {
618  }
619  else
620  handled = false;
621  }
622 
623  if (!handled && MythScreenType::keyPressEvent(event))
624  handled = true;
625 
626  return handled;
627 }
628 
630 {
632  {
634  {
637 
638  if (m_fullScreenName)
640 
642  m_fullPreviewShowing = false;
643  }
644  else
645  {
647  auto *info = item->GetData().value<ThemeInfo*>();
648  if (info)
649  {
651  {
652  m_fullScreenPreview->SetFilename(info->GetPreviewPath());
654  }
655 
656  if (m_fullScreenName)
657  m_fullScreenName->SetText(info->GetName());
658 
659  m_fullPreviewStateType->DisplayState("fullscreen");
660  m_fullPreviewShowing = true;
661  }
662  }
663  }
664 }
665 
667 {
668  if (gCoreContext->GetBoolSetting("ThemeUpdateNofications", true))
669  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
670  else
671  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
672 }
673 
675 {
676  LOG(VB_GUI, LOG_INFO, LOC + "Forcing remote theme list refresh");
678  gCoreContext->SaveSetting("ThemeInfoDownloadFailures", 0);
680 }
681 
683 {
685  if (current)
687 }
688 
690 {
691  auto *info = item->GetData().value<ThemeInfo *>();
692 
693  if (!info)
694  return;
695 
696  if (!info->GetDownloadURL().isEmpty())
697  {
698  QString testFile = m_userThemeDir + "/.test";
699  QFile test(testFile);
700  if (test.open(QIODevice::WriteOnly))
701  test.remove();
702  else
703  {
704  ShowOkPopup(tr("Unable to install theme, %1 themes directory is "
705  "not writable.").arg(m_userThemeDir));
706  return;
707  }
708 
709  QString downloadURL = info->GetDownloadURL();
710  QFileInfo qfile(downloadURL);
711  QString baseName = qfile.fileName();
712 
713  if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
714  {
715 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
716  QStringList tokens =
717  gCoreContext->GetSetting("ThemeDownloadURL")
718  .split(";", QString::SkipEmptyParts);
719 #else
720  QStringList tokens =
721  gCoreContext->GetSetting("ThemeDownloadURL")
722  .split(";", Qt::SkipEmptyParts);
723 #endif
724  QString origURL = downloadURL;
725  downloadURL.replace(tokens[0], tokens[1]);
726  LOG(VB_FILE, LOG_WARNING, LOC +
727  QString("Theme download URL overridden from %1 to %2.")
728  .arg(origURL, downloadURL));
729  }
730 
731  OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
732  m_downloadTheme = info;
733 #if 0
735  "Temp", baseName);
737 #else
738  QString localFile = GetConfDir() + "/tmp/" + baseName;
739  GetMythDownloadManager()->queueDownload(downloadURL, localFile, this);
740  m_downloadFile = localFile;
742 #endif
743  }
744  else
745  {
746  gCoreContext->SaveSetting("Theme", info->GetDirectoryName());
747  GetMythMainWindow()->JumpTo("Reload Theme");
748  }
749 }
750 
752 {
753  auto *info = item->GetData().value<ThemeInfo*>();
754 
755  if (!info)
756  return;
757 
758  QFileInfo preview(info->GetPreviewPath());
759  InfoMap infomap;
760  info->ToMap(infomap);
761  SetTextFromMap(infomap);
762  if (m_preview)
763  {
764  if (preview.exists())
765  {
766  m_preview->SetFilename(info->GetPreviewPath());
767  m_preview->Load();
768  }
769  else
770  m_preview->Reset();
771  }
773  {
775  {
776  if (preview.exists())
777  {
778  m_fullScreenPreview->SetFilename(info->GetPreviewPath());
780  }
781  else
783  }
784 
785  if (m_fullScreenName)
786  m_fullScreenName->SetText(info->GetName());
787  }
788 
789  MythUIStateType *themeLocation =
790  dynamic_cast<MythUIStateType*>(GetChild("themelocation"));
791  if (themeLocation)
792  {
793  if (info->GetDownloadURL().isEmpty())
794  themeLocation->DisplayState("local");
795  else
796  themeLocation->DisplayState("remote");
797  }
798 
799  MythUIStateType *aspectState =
800  dynamic_cast<MythUIStateType*>(GetChild("aspectstate"));
801  if (aspectState)
802  aspectState->DisplayState(info->GetAspect());
803 }
804 
805 void ThemeChooser::updateProgressBar(int bytesReceived,
806  int bytesTotal)
807 {
808  MythUIProgressBar *progressBar =
809  dynamic_cast<MythUIProgressBar *>(GetChild("downloadprogressbar"));
810 
811  if (!progressBar)
812  return;
813 
814  progressBar->SetUsed(bytesReceived);
815  progressBar->SetTotal(bytesTotal);
816 }
817 
819 {
820  if (e->type() == MythEvent::MythEventMessage)
821  {
822  auto *me = dynamic_cast<MythEvent *>(e);
823  if (me == nullptr)
824  return;
825 
826 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
827  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
828 #else
829  QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
830 #endif
831 
832  if (tokens.isEmpty())
833  return;
834 
835  if (tokens[0] == "DOWNLOAD_FILE")
836  {
837  QStringList args = me->ExtraDataList();
838  if ((m_downloadState == dsIdle) ||
839  (tokens.size() != 2) ||
840  (!m_downloadTheme) ||
841  (args[1] != m_downloadFile))
842  return;
843 
844  if (tokens[1] == "UPDATE")
845  {
846  updateProgressBar(args[2].toInt(), args[3].toInt());
847  }
848  else if (tokens[1] == "FINISHED")
849  {
850  bool remoteFileIsLocal = false;
851  int fileSize = args[2].toInt();
852  int errorCode = args[4].toInt();
853 
854  CloseBusyPopup();
855 
856  QFileInfo file(m_downloadFile);
858  (m_downloadFile.startsWith("myth://")))
859  {
860  // The backend download is finished so start the
861  // frontend download
862  if ((errorCode == 0) &&
863  (fileSize > 0))
864  {
866  QString localFile = GetConfDir() + "/tmp/" +
867  file.fileName();
868  file.setFile(localFile);
869 
870  if (file.exists())
871  {
872  remoteFileIsLocal = true;
873  m_downloadFile = localFile;
874  }
875  else
876  {
878  m_downloadFile, localFile, this);
879  OpenBusyPopup(tr("Copying %1 Theme Package")
880  .arg(m_downloadTheme->GetName()));
881  m_downloadFile = localFile;
882  return;
883  }
884  }
885  else
886  {
888  ShowOkPopup(tr("ERROR downloading theme package on master backend."));
889  }
890  }
891 
893  (file.exists()))
894  {
895  // The frontend download is finished
896  if ((errorCode == 0) &&
897  (fileSize > 0))
898  {
900  auto *extractThread =
904  extractThread, "ThemeExtract");
905 
906  if (!remoteFileIsLocal)
908 
909  OpenBusyPopup(tr("Installing %1 Theme")
910  .arg(m_downloadTheme->GetName()));
911  }
912  else
913  {
915  ShowOkPopup(tr("ERROR downloading theme package from master backend."));
916  }
917  }
918  }
919  }
920  else if ((me->Message() == "THEME_INSTALLED") &&
921  (m_downloadTheme) &&
923  {
925  CloseBusyPopup();
926  QStringList args = me->ExtraDataList();
927 
928  if (!args.isEmpty() && !args[0].isEmpty())
929  QFile::remove(args[0]);
930 
931  QString event = QString("THEME_INSTALLED PATH %1")
932  .arg(m_userThemeDir +
935 
937 
938  // Send a message to ourself so we trigger a reload our next chance
939  auto *me2 = new MythEvent("THEME_RELOAD");
940  qApp->postEvent(this, me2);
941  }
942  else if ((me->Message() == "THEME_RELOAD") &&
943  (m_downloadState == dsIdle))
944  {
945  GetMythMainWindow()->JumpTo("Reload Theme");
946  }
947  }
948 }
949 
951 {
953  if (!current)
954  {
955  ShowOkPopup(tr("Error, no theme selected."));
956  return;
957  }
958 
959  auto *info = current->GetData().value<ThemeInfo *>();
960  if (!info)
961  {
962  ShowOkPopup(tr("Error, unable to find current theme."));
963  return;
964  }
965 
966  if (!info->GetPreviewPath().startsWith(m_userThemeDir))
967  {
968  ShowOkPopup(tr("%1 is not a user-installed theme and can not "
969  "be deleted.").arg(info->GetName()));
970  return;
971  }
972 
973  removeThemeDir(m_userThemeDir + info->GetDirectoryName());
974 
976 }
977 
978 bool ThemeChooser::removeThemeDir(const QString &dirname)
979 {
980  if ((!dirname.startsWith(m_userThemeDir)) &&
981  (!dirname.startsWith(GetMythUI()->GetThemeCacheDir())))
982  return true;
983 
984  QDir dir(dirname);
985 
986  if (!dir.exists())
987  return true;
988 
989  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
990  QFileInfoList list = dir.entryInfoList();
991 
992  for (const auto & fi : qAsConst(list))
993  {
994  if (fi.isFile() && !fi.isSymLink())
995  {
996  if (!QFile::remove(fi.absoluteFilePath()))
997  return false;
998  }
999  else if (fi.isDir() && !fi.isSymLink())
1000  {
1001  if (!removeThemeDir(fi.absoluteFilePath()))
1002  return false;
1003  }
1004  }
1005 
1006  return dir.rmdir(dirname);
1007 }
1008 
1010 
1012  m_updateTimer(new QTimer(this))
1013 {
1014  QString version = GetMythSourcePath();
1015 
1016  if (!version.isEmpty() && !version.startsWith("fixes/"))
1017  {
1018  // Treat devel branches as master
1019  m_mythVersions << "trunk";
1020  }
1021  else
1022  {
1023  version = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
1024  version.remove(QRegularExpression("\\.[0-9]{8,}.*"));
1025 
1026  // If a version of the theme for this tag exists, use it...
1027  static const QRegularExpression subexp
1028  { "v[0-9]+\\.([0-9]+)-*", QRegularExpression::CaseInsensitiveOption };
1029  auto match = subexp.match(GetMythSourceVersion());
1030  if (match.hasMatch())
1031  {
1032  for (int idx = match.capturedView(1).toInt(); idx > 0; --idx)
1033  m_mythVersions << version + "." + QString::number(idx);
1034  }
1036  }
1037 
1040  "remotethemes/themes.zip",
1041  "Temp");
1042 
1043  gCoreContext->SaveSetting("ThemeUpdateStatus", "");
1044 
1046 
1047  if (qEnvironmentVariableIsSet("MYTHTV_DEBUGMDM"))
1048  {
1049  LOG(VB_GENERAL, LOG_INFO, "Checking for theme updates every minute");
1050  m_updateTimer->start(1min);
1051  }
1052  else
1053  {
1054  LOG(VB_GENERAL, LOG_INFO, "Checking for theme updates every hour");
1055  m_updateTimer->start(1h);
1056  }
1057 
1058  // Run once 15 seconds from now
1059  QTimer::singleShot(15s, this, &ThemeUpdateChecker::checkForUpdate);
1060 }
1061 
1063 {
1064  if (m_updateTimer)
1065  {
1066  m_updateTimer->stop();
1067  delete m_updateTimer;
1068  m_updateTimer = nullptr;
1069  }
1070 }
1071 
1073 {
1074  if (GetMythUI()->GetCurrentLocation(false, true) != "mainmenu")
1075  return;
1076 
1077  ThemeInfo *localTheme = nullptr;
1078 
1080  {
1081  QStringList::iterator Iversion;
1082 
1083  for (Iversion = m_mythVersions.begin();
1084  Iversion != m_mythVersions.end(); ++Iversion)
1085  {
1086 
1087  QString remoteThemeDir =
1090  QString("remotethemes/%1/%2")
1091  .arg(*Iversion,
1092  GetMythUI()->GetThemeName()),
1093  "Temp");
1094 
1095  QString infoXML = remoteThemeDir;
1096  infoXML.append("/themeinfo.xml");
1097 
1098  LOG(VB_GUI, LOG_INFO, QString("ThemeUpdateChecker Loading '%1'")
1099  .arg(infoXML));
1100 
1101  if (RemoteFile::Exists(infoXML))
1102  {
1103  int locMaj = 0;
1104  int locMin = 0;
1105 
1106  auto *remoteTheme = new ThemeInfo(remoteThemeDir);
1107  if (!remoteTheme || remoteTheme->GetType() & THEME_UNKN)
1108  {
1109  LOG(VB_GENERAL, LOG_ERR,
1110  QString("ThemeUpdateChecker::checkForUpdate(): "
1111  "Unable to create ThemeInfo for %1")
1112  .arg(infoXML));
1113  delete remoteTheme;
1114  remoteTheme = nullptr;
1115  return;
1116  }
1117 
1118  if (!localTheme)
1119  {
1120  localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
1121  if (!localTheme || localTheme->GetType() & THEME_UNKN)
1122  {
1123  LOG(VB_GENERAL, LOG_ERR,
1124  "ThemeUpdateChecker::checkForUpdate(): "
1125  "Unable to create ThemeInfo for current theme");
1126  delete localTheme;
1127  localTheme = nullptr;
1128  return;
1129  }
1130  locMaj = localTheme->GetMajorVersion();
1131  locMin = localTheme->GetMinorVersion();
1132  }
1133 
1134  int rmtMaj = remoteTheme->GetMajorVersion();
1135  int rmtMin = remoteTheme->GetMinorVersion();
1136 
1137  delete remoteTheme;
1138  remoteTheme = nullptr;
1139 
1140  if ((rmtMaj > locMaj) ||
1141  ((rmtMaj == locMaj) &&
1142  (rmtMin > locMin)))
1143  {
1145  QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
1146  .arg(rmtMaj).arg(rmtMin);
1147 
1148  QString status = gCoreContext->GetSetting
1149  ("ThemeUpdateStatus");
1150  QString currentLocation = GetMythUI()->GetCurrentLocation
1151  (false, true);
1152 
1153  if ((!status.startsWith(m_lastKnownThemeVersion)) &&
1154  (currentLocation == "mainmenu"))
1155  {
1156  m_currentVersion = QString("%1.%2")
1157  .arg(locMaj).arg(locMin);
1158  m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);
1159 
1160  gCoreContext->SaveSetting("ThemeUpdateStatus",
1162  + " notified");
1163 
1164  QString message = tr("Version %1 of the %2 theme is now "
1165  "available in the Theme Chooser. "
1166  "The currently installed version "
1167  "is %3.")
1168  .arg(m_newVersion,
1169  GetMythUI()->GetThemeName(),
1171 
1172  ShowOkPopup(message);
1173  break;
1174  }
1175  }
1176  }
1177  }
1178  }
1179 
1180  delete localTheme;
1181 }
1182 
1183 /* vim: set expandtab tabstop=4 shiftwidth=4: */
GetThemesParentDir
QString GetThemesParentDir(void)
Definition: mythdirs.cpp:225
MythScreenType::LoadInBackground
void LoadInBackground(const QString &message="")
Definition: mythscreentype.cpp:285
build_compdb.args
args
Definition: build_compdb.py:11
MythDialogBox::SetReturnEvent
void SetReturnEvent(QObject *retobject, const QString &resultid)
Definition: mythdialogbox.cpp:301
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:78
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1590
MythScreenType::SetBusyPopupMessage
void SetBusyPopupMessage(const QString &message)
Definition: mythscreentype.cpp:344
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:103
ThemeChooser::m_themeNameInfos
QMap< QString, ThemeInfo * > m_themeNameInfos
Definition: themechooser.h:86
MythCoreContext::GetMasterHostName
QString GetMasterHostName(void)
Definition: mythcorecontext.cpp:831
ThemeUpdateChecker::m_lastKnownThemeVersion
QString m_lastKnownThemeVersion
Definition: themechooser.h:113
MythUIButtonList::SetValueByData
void SetValueByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1542
ThemeChooser
View and select installed themes.
Definition: themechooser.h:27
mythuitext.h
ThemeChooser::m_themes
MythUIButtonList * m_themes
Definition: themechooser.h:74
mythuiprogressbar.h
MythUIImage
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:97
MythUIText::Reset
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitext.cpp:83
ThemeExtractThread::run
void run() override
Definition: themechooser.cpp:58
MythUIButtonListItem::DisplayState
void DisplayState(const QString &state, const QString &name)
Definition: mythuibuttonlist.cpp:3506
ThemeInfo::GetAspect
QString GetAspect() const
Definition: themeinfo.h:28
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
extractZIP
bool extractZIP(QString &zipFile, const QString &outDir)
Definition: mythcoreutil.cpp:74
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:131
mythscreenstack.h
MythMainWindow::JumpTo
void JumpTo(const QString &Destination, bool Pop=true)
Definition: mythmainwindow.cpp:1460
MythUIImage::Load
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
Definition: mythuiimage.cpp:968
MythUIButtonList::itemSelected
void itemSelected(MythUIButtonListItem *item)
MythEvent
This class is used as a container for messages.
Definition: mythevent.h:16
mythcoreutil.h
ThemeChooser::customEvent
void customEvent(QEvent *e) override
Definition: themechooser.cpp:818
MythDownloadManager::queueDownload
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
Definition: mythdownloadmanager.cpp:393
MythUIProgressBar::SetUsed
void SetUsed(int value)
Definition: mythuiprogressbar.cpp:59
ThemeInfo::GetMinorVersion
int GetMinorVersion() const
Definition: themeinfo.h:37
mythdialogbox.h
MythScreenStack
Definition: mythscreenstack.h:16
ThemeChooser::m_popupMenu
MythDialogBox * m_popupMenu
Definition: themechooser.h:93
ThemeChooser::refreshDownloadableThemes
void refreshDownloadableThemes(void)
Definition: themechooser.cpp:674
ThemeChooser::m_refreshDownloadableThemes
bool m_refreshDownloadableThemes
Definition: themechooser.h:83
MythScreenType::OpenBusyPopup
void OpenBusyPopup(const QString &message="")
Definition: mythscreentype.cpp:319
MythUIGroup
Create a group of widgets.
Definition: mythuigroup.h:11
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythDialogBox::Closed
void Closed(QString, int)
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:44
mythuistatetype.h
GetCacheDir
QString GetCacheDir(void)
Returns the base directory for all cached files.
Definition: mythdirs.cpp:234
ThemeUpdateChecker::m_newVersion
QString m_newVersion
Definition: themechooser.h:115
MythUIStateType::Reset
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuistatetype.cpp:197
mythsystemevent.h
build_compdb.file
file
Definition: build_compdb.py:55
ThemeChooser::popupClosed
void popupClosed(const QString &which, int result)
Definition: themechooser.cpp:589
MythUIImage::Reset
void Reset(void) override
Reset the image back to the default defined in the theme.
Definition: mythuiimage.cpp:645
ThemeUpdateChecker::m_updateTimer
QTimer * m_updateTimer
Definition: themechooser.h:110
ThemeChooser::updateProgressBar
void updateProgressBar(int bytesReceived, int bytesTotal)
Definition: themechooser.cpp:805
ThemeInfo::GetDownloadURL
QString GetDownloadURL() const
Definition: themeinfo.h:39
THEME_UNKN
@ THEME_UNKN
Definition: themeinfo.h:15
mythuibuttonlist.h
mythuiimage.h
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
MythEvent::Message
const QString & Message() const
Definition: mythevent.h:65
ThemeUpdateChecker::m_mythVersions
QStringList m_mythVersions
Definition: themechooser.h:111
MythScreenType::GetFocusWidget
MythUIType * GetFocusWidget(void) const
Definition: mythscreentype.cpp:112
ThemeExtractThread
Definition: themechooser.cpp:49
mythversion.h
programtypes.h
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
MythObservable::addListener
void addListener(QObject *listener)
Add a listener to the observable.
Definition: mythobservable.cpp:38
ThemeChooser::ThemeChooser
ThemeChooser(MythScreenStack *parent, const QString &name="ThemeChooser")
Creates a new ThemeChooser Screen.
Definition: themechooser.cpp:77
MythScreenType::ResetBusyPopup
void ResetBusyPopup(void)
Definition: mythscreentype.cpp:350
MythUIButtonListItem
Definition: mythuibuttonlist.h:27
ThemeInfo::GetName
QString GetName() const
Definition: themeinfo.h:30
ThemeChooser::showPopupMenu
void showPopupMenu(void)
Definition: themechooser.cpp:520
MythScreenType::ReloadInBackground
void ReloadInBackground(void)
Definition: mythscreentype.cpp:313
mythdate.h
MythUIProgressBar::SetTotal
void SetTotal(int value)
Definition: mythuiprogressbar.cpp:71
mythlogging.h
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:784
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
ThemeExtractThread::m_destDir
QString m_destDir
Definition: themechooser.cpp:69
LOC
#define LOC
Definition: themechooser.cpp:42
MythUIButtonList::itemClicked
void itemClicked(MythUIButtonListItem *item)
MythMainWindow::TranslateKeyPress
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Definition: mythmainwindow.cpp:1116
remotefile.h
ThemeChooser::Create
bool Create(void) override
Definition: themechooser.cpp:98
MythUIProgressBar
Progress bar widget.
Definition: mythuiprogressbar.h:12
MythCoreContext::SendSystemEvent
void SendSystemEvent(const QString &msg)
Definition: mythcorecontext.cpp:1556
MythScreenType::SetFocusWidget
bool SetFocusWidget(MythUIType *widget=nullptr)
Definition: mythscreentype.cpp:117
MythDialogBox::AddButton
void AddButton(const QString &title)
Definition: mythdialogbox.h:198
MythDialogBox
Basic menu dialog, message and a list of options.
Definition: mythdialogbox.h:166
ThemeChooser::loadThemeInfo
ThemeInfo * loadThemeInfo(const QFileInfo &theme)
Definition: themechooser.cpp:494
MythDialogBox::Create
bool Create(void) override
Definition: mythdialogbox.cpp:127
ThemeChooser::m_downloadTheme
ThemeInfo * m_downloadTheme
Definition: themechooser.h:89
MythScreenType::BuildFocusList
void BuildFocusList(void)
Definition: mythscreentype.cpp:205
MythUIComposite::SetTextFromMap
virtual void SetTextFromMap(const InfoMap &infoMap)
Definition: mythuicomposite.cpp:9
ThemeUpdateChecker::checkForUpdate
void checkForUpdate(void)
Definition: themechooser.cpp:1072
ThemeChooser::Load
void Load(void) override
Load data which will ultimately be displayed on-screen or used to determine what appears on-screen (S...
Definition: themechooser.cpp:142
MythDownloadManager::download
bool download(const QString &url, const QString &dest, bool reload=false)
Downloads a URL to a file in blocking mode.
Definition: mythdownloadmanager.cpp:430
ThemeChooser::dsDownloadingOnFrontend
@ dsDownloadingOnFrontend
Definition: themechooser.h:65
ThemeInfo::GetPreviewPath
QString GetPreviewPath() const
Definition: themeinfo.h:34
StorageGroup::GetFirstDir
QString GetFirstDir(bool appendSlash=false) const
Definition: storagegroup.cpp:189
ThemeInfo
Definition: themeinfo.h:21
storagegroup.h
ThemeChooser::dsExtractingTheme
@ dsExtractingTheme
Definition: themechooser.h:66
ThemeChooser::saveAndReload
void saveAndReload(void)
Definition: themechooser.cpp:682
RemoteFile::DeleteFile
static bool DeleteFile(const QString &url)
Definition: remotefile.cpp:418
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3587
MythUILocation::GetCurrentLocation
QString GetCurrentLocation(bool FullPath=false, bool MainStackOnly=true)
Definition: mythuilocation.cpp:20
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:936
GetMythSourcePath
const char * GetMythSourcePath()
Definition: mythcoreutil.cpp:297
ThemeChooser::m_userThemeDir
QString m_userThemeDir
Definition: themechooser.h:84
UIUtilDisp::Assign
static bool Assign(ContainerType *container, UIType *&item, const QString &name, bool *err=nullptr)
Definition: mythuiutils.h:27
mythuigroup.h
MythUIButtonListItem::SetTextFromMap
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
Definition: mythuibuttonlist.cpp:3288
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:930
ThemeExtractThread::m_parent
ThemeChooser * m_parent
Definition: themechooser.cpp:67
mythburn.themeName
string themeName
Definition: mythburn.py:218
mthreadpool.h
mythuihelper.h
ThemeUpdateChecker::~ThemeUpdateChecker
~ThemeUpdateChecker(void) override
Definition: themechooser.cpp:1062
MYTH_BINARY_VERSION
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:15
MythCoreContext::GetMasterServerPort
static int GetMasterServerPort(void)
Returns the Master Backend control port If no master server port has been defined in the database,...
Definition: mythcorecontext.cpp:1004
ThemeChooser::toggleFullscreenPreview
void toggleFullscreenPreview(void)
Definition: themechooser.cpp:629
ThemeUpdateChecker::ThemeUpdateChecker
ThemeUpdateChecker(void)
Definition: themechooser.cpp:1011
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:30
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:397
ThemeInfo::GetType
int GetType() const
Definition: themeinfo.h:35
themechooser.h
ThemeChooser::m_fullPreviewShowing
bool m_fullPreviewShowing
Definition: themechooser.h:77
ThemeChooser::m_downloadState
DownloadState m_downloadState
Definition: themechooser.h:91
ThemeChooser::m_themeStatuses
QMap< QString, QString > m_themeStatuses
Definition: themechooser.h:88
mythcorecontext.h
ThemeChooser::dsDownloadingOnBackend
@ dsDownloadingOnBackend
Definition: themechooser.h:64
ThemeChooser::m_themeFileNameInfos
QMap< QString, ThemeInfo * > m_themeFileNameInfos
Definition: themechooser.h:87
ThemeChooser::LoadVersion
void LoadVersion(const QString &version, QStringList &themesSeen, bool alert_user)
Definition: themechooser.cpp:229
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:692
MythUIButtonListItem::SetImage
void SetImage(MythImage *image, const QString &name="")
Sets an image directly, should only be used in special circumstances since it bypasses the cache.
Definition: mythuibuttonlist.cpp:3405
std
Definition: mythchrono.h:23
ThemeUpdateChecker::m_currentVersion
QString m_currentVersion
Definition: themechooser.h:114
MythUIText::SetText
virtual void SetText(const QString &text)
Definition: mythuitext.cpp:133
ThemeChooser::m_fullScreenName
MythUIText * m_fullScreenName
Definition: themechooser.h:79
ThemeInfo::GetMajorVersion
int GetMajorVersion() const
Definition: themeinfo.h:36
ThemeChooser::m_fullScreenPreview
MythUIImage * m_fullScreenPreview
Definition: themechooser.h:80
MythUIButtonList::Reset
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuibuttonlist.cpp:114
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:104
StorageGroup
Definition: storagegroup.h:11
build_compdb.action
action
Definition: build_compdb.py:9
ThemeChooser::toggleThemeUpdateNotifications
static void toggleThemeUpdateNotifications(void)
Definition: themechooser.cpp:666
ThemeChooser::m_fullPreviewStateType
MythUIStateType * m_fullPreviewStateType
Definition: themechooser.h:78
ThemeUpdateChecker::m_infoPackage
QString m_infoPackage
Definition: themechooser.h:112
ThemeChooser::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: themechooser.cpp:597
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:322
GetMythSourceVersion
const char * GetMythSourceVersion()
Definition: mythcoreutil.cpp:292
ThemeChooser::~ThemeChooser
~ThemeChooser() override
Definition: themechooser.cpp:87
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:862
ThemeExtractThread::ThemeExtractThread
ThemeExtractThread(ThemeChooser *parent, QString srcFile, QString destDir)
Definition: themechooser.cpp:52
MythCoreContext::SaveSetting
void SaveSetting(const QString &key, int newValue)
Definition: mythcorecontext.cpp:905
downloadURL
static bool downloadURL(const QString &url, QByteArray *buffer, QString &finalURL)
Definition: httplivestreambuffer.cpp:96
ThemeChooser::m_downloadFile
QString m_downloadFile
Definition: themechooser.h:90
MythUIImage::SetFilename
void SetFilename(const QString &filename)
Must be followed by a call to Load() to load the image.
Definition: mythuiimage.cpp:676
mythdownloadmanager.h
GetMythUI
MythUIHelper * GetMythUI()
Definition: mythuihelper.cpp:66
ThemeChooser::m_infoList
QFileInfoList m_infoList
Definition: themechooser.h:82
MThreadPool::globalInstance
static MThreadPool * globalInstance(void)
Definition: mthreadpool.cpp:317
mythmainwindow.h
ThemeChooser::Init
void Init(void) override
Used after calling Load() to assign data to widgets and other UI initilisation which is prohibited in...
Definition: themechooser.cpp:424
ThemeChooser::dsIdle
@ dsIdle
Definition: themechooser.h:63
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3582
MythScreenStack::AddScreen
virtual void AddScreen(MythScreenType *screen, bool allowFade=true)
Definition: mythscreenstack.cpp:50
ShowOkPopup
MythConfirmationDialog * ShowOkPopup(const QString &message, bool showCancel)
Non-blocking version of MythPopupBox::showOkPopup()
Definition: mythdialogbox.cpp:558
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:81
THEME_UI
@ THEME_UI
Definition: themeinfo.h:16
MythCoreContext::SaveSettingOnHost
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
Definition: mythcorecontext.cpp:915
ThemeChooser::removeTheme
void removeTheme(void)
Definition: themechooser.cpp:950
RemoteDownloadFile
QString RemoteDownloadFile(const QString &url, const QString &storageGroup, const QString &filename)
Definition: mythcoreutil.cpp:278
ThemeInfo::GetDirectoryName
QString GetDirectoryName() const
Definition: themeinfo.h:43
ThemeInfo::ToMap
void ToMap(InfoMap &infoMap) const
Definition: themeinfo.cpp:252
MythObservable::removeListener
void removeListener(QObject *listener)
Remove a listener to the observable.
Definition: mythobservable.cpp:55
sortThemeNames
static bool sortThemeNames(const QFileInfo &s1, const QFileInfo &s2)
Definition: themechooser.cpp:92
ThemeChooser::m_preview
MythUIImage * m_preview
Definition: themechooser.h:75
MythScreenType::CloseBusyPopup
void CloseBusyPopup(void)
Definition: mythscreentype.cpp:337
MythUIStateType
This widget is used for grouping other widgets for display when a particular named state is called....
Definition: mythuistatetype.h:22
MThreadPool::start
void start(QRunnable *runnable, const QString &debugName, int priority=0)
Definition: mthreadpool.cpp:352
ThemeExtractThread::m_srcFile
QString m_srcFile
Definition: themechooser.cpp:68
MythUIStateType::DisplayState
bool DisplayState(const QString &name)
Definition: mythuistatetype.cpp:84
ThemeChooser::removeThemeDir
bool removeThemeDir(const QString &dirname)
Definition: themechooser.cpp:978
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:922
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145
ThemeChooser::itemChanged
void itemChanged(MythUIButtonListItem *item)
Definition: themechooser.cpp:751