MythTV  master
themechooser.cpp
Go to the documentation of this file.
1 // C++ headers
2 #include <chrono>
3 
4 // Qt headers
5 #include <QCoreApplication>
6 #include <QRegularExpression>
7 #include <QRunnable>
8 
9 // MythTV headers
13 #include "libmythbase/mythdate.h"
18 #include "libmythbase/remotefile.h"
20 #include "libmythbase/unziputil.h" // for extractZIP
26 #include "libmythui/mythuigroup.h"
27 #include "libmythui/mythuihelper.h"
28 #include "libmythui/mythuiimage.h"
31 #include "libmythui/mythuitext.h"
32 
33 // Theme Chooser headers
34 #include "themechooser.h"
35 
36 #if QT_VERSION < QT_VERSION_CHECK(5,15,2)
37 #define capturedView capturedRef
38 #endif
39 
40 #define LOC QString("ThemeChooser: ")
41 #define LOC_WARN QString("ThemeChooser, Warning: ")
42 #define LOC_ERR QString("ThemeChooser, Error: ")
43 
44 static const QRegularExpression kVersionDateRE { "\\.[0-9]{8,}.*" };
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  MythVersion = MYTH_BINARY_VERSION; // Example: 29.20161017-1
200  // Remove the date part and the rest, eg 29.20161017-1 -> 29
201  MythVersion.remove(kVersionDateRE);
202  LOG(VB_GUI, LOG_INFO, QString("Loading themes for %1").arg(MythVersion));
203  LoadVersion(MythVersion, themesSeen, true);
204 
205  // If a version of the theme for this tag exists, use it...
206  // MYTH_SOURCE_VERSION - examples v29-pre-574-g92517f5, v29-Pre, v29.1-21-ge26a33c
207  static const QRegularExpression subexp
208  { "v[0-9]+\\.([0-9]+)-*", QRegularExpression::CaseInsensitiveOption };
209  // This captures the subversion, i.e. the number after a dot
210  match = subexp.match(GetMythSourceVersion());
211  if (match.hasMatch())
212  {
213  QString subversion;
214  for (int idx = match.capturedView(1).toInt(); idx > 0; --idx)
215  {
216  subversion = MythVersion + "." + QString::number(idx);
217  LOG(VB_GUI, LOG_INFO, QString("Loading themes for %1").arg(subversion));
218  LoadVersion(subversion, themesSeen, false);
219  }
220  }
221  }
222 
223  ResetBusyPopup();
224 
225  std::sort(m_infoList.begin(), m_infoList.end(), sortThemeNames);
226 }
227 
228 void ThemeChooser::LoadVersion(const QString &version,
229  QStringList &themesSeen, bool alert_user)
230 {
231  QString remoteThemesFile = GetConfDir();
232  remoteThemesFile.append("/tmp/themes.zip");
233  QString themeSite = QString("%1/%2")
234  .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
235  "http://themes.mythtv.org/themes/repository"), version);
236  QString destdir = GetCacheDir().append("/themechooser/");
237  QString versiondir = QString("%1/%2").arg(destdir, version);
238  QDir remoteThemesDir(versiondir);
239 
240  int downloadFailures =
241  gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
242  if (QFile::exists(remoteThemesFile))
243  {
244  QFileInfo finfo(remoteThemesFile);
245  if (finfo.lastModified().toUTC() <
246  MythDate::current().addSecs(-600))
247  {
248  LOG(VB_GUI, LOG_INFO, LOC +
249  QString("%1 is over 10 minutes old, forcing "
250  "remote theme list download").arg(remoteThemesFile));
252  }
253 
254  if (!remoteThemesDir.exists())
256  }
257  else if (downloadFailures < 2) // (and themes.zip does not exist)
258  {
259  LOG(VB_GUI, LOG_INFO, LOC +
260  QString("%1 does not exist, forcing remote theme "
261  "list download").arg(remoteThemesFile));
263  }
264 
266  {
267  QFile test(remoteThemesFile);
268  if (test.open(QIODevice::WriteOnly))
269  test.remove();
270  else
271  {
272  ShowOkPopup(tr("Unable to create '%1'").arg(remoteThemesFile));
273  return;
274  }
275 
276  SetBusyPopupMessage(tr("Refreshing Downloadable Themes Information"));
277 
278  QString url = themeSite;
279  url.append("/themes.zip");
280  if (!removeThemeDir(versiondir))
281  ShowOkPopup(tr("Unable to remove '%1'").arg(versiondir));
282  QDir dir;
283  if (!dir.mkpath(destdir))
284  ShowOkPopup(tr("Unable to create '%1'").arg(destdir));
285  bool result = GetMythDownloadManager()->download(url, remoteThemesFile, true);
286 
287  LOG(VB_GUI, LOG_INFO, LOC +
288  QString("Downloading '%1' to '%2'").arg(url, remoteThemesFile));
289 
290  SetBusyPopupMessage(tr("Extracting Downloadable Themes Information"));
291 
292  if (!result || !extractZIP(remoteThemesFile, destdir))
293  {
294  QFile::remove(remoteThemesFile);
295 
296  downloadFailures++;
297  gCoreContext->SaveSetting("ThemeInfoDownloadFailures",
298  downloadFailures);
299 
300  if (!result)
301  {
302  LOG(VB_GUI, LOG_ERR, LOC +
303  QString("Failed to download '%1'").arg(url));
304  if (alert_user)
305  ShowOkPopup(tr("Failed to download '%1'").arg(url));
306  }
307  else
308  {
309  LOG(VB_GUI, LOG_ERR, LOC +
310  QString("Failed to unzip '%1' to '%2'")
311  .arg(remoteThemesFile, destdir));
312  if (alert_user)
313  ShowOkPopup(tr("Failed to unzip '%1' to '%2'")
314  .arg(remoteThemesFile, destdir));
315  }
316  }
317  else
318  {
319  LOG(VB_GUI, LOG_INFO, LOC +
320  QString("Unzipped '%1' to '%2'")
321  .arg(remoteThemesFile, destdir));
322  }
323  }
324 
325  QDir themes;
326  themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
327  themes.setSorting(QDir::Name | QDir::IgnoreCase);
328 
329  if ((QFile::exists(remoteThemesFile)) &&
330  (remoteThemesDir.exists()))
331  {
332  SetBusyPopupMessage(tr("Loading Downloadable Themes"));
333 
334  LOG(VB_GUI, LOG_INFO, LOC +
335  QString("%1 and %2 exist, using cached remote themes list")
336  .arg(remoteThemesFile, remoteThemesDir.absolutePath()));
337 
338  QString themesPath = remoteThemesDir.absolutePath();
339  themes.setPath(themesPath);
340 
341  QFileInfoList downloadableThemes = themes.entryInfoList();
342  for (const auto & dtheme : qAsConst(downloadableThemes))
343  {
344  QString dirName = dtheme.fileName();
345  QString themeName = dirName;
346  QString remoteDir = themeSite;
347  remoteDir.append("/").append(dirName);
348  QString localDir = themes.absolutePath();
349  localDir.append("/").append(dirName);
350 
351  ThemeInfo remoteTheme(dtheme.absoluteFilePath());
352 
353  if (themesSeen.contains(dirName))
354  {
355  ThemeInfo *localTheme = m_themeNameInfos[dirName];
356 
357  themeName = remoteTheme.GetName();
358 
359  int rmtMaj = remoteTheme.GetMajorVersion();
360  int rmtMin = remoteTheme.GetMinorVersion();
361  int locMaj = localTheme->GetMajorVersion();
362  int locMin = localTheme->GetMinorVersion();
363 
364  if ((rmtMaj > locMaj) ||
365  ((rmtMaj == locMaj) &&
366  (rmtMin > locMin)))
367  {
368  if (loadThemeInfo(dtheme))
369  {
370  LOG(VB_GUI, LOG_DEBUG, LOC +
371  QString("'%1' old version %2.%3, new version %4.%5")
372  .arg(themeName).arg(locMaj).arg(locMin)
373  .arg(rmtMaj).arg(rmtMin));
374 
375  m_infoList << dtheme;
376  m_themeStatuses[themeName] = "updateavailable";
377 
378  QFileInfo finfo(remoteTheme.GetPreviewPath());
380  remoteDir.append("/").append(finfo.fileName()),
381  localDir.append("/").append(finfo.fileName()),
382  nullptr);
383  }
384  }
385  else if ((rmtMaj == locMaj) &&
386  (rmtMin == locMin))
387  {
388  LOG(VB_GUI, LOG_DEBUG, LOC +
389  QString("'%1' up to date (%2.%3)")
390  .arg(themeName).arg(locMaj).arg(locMin));
391 
392  m_themeStatuses[themeName] = "uptodate";
393  }
394  }
395  else
396  {
397  LOG(VB_GUI, LOG_DEBUG, LOC +
398  QString("'%1' (%2.%3) available")
399  .arg(themeName)
400  .arg(remoteTheme.GetMajorVersion())
401  .arg(remoteTheme.GetMinorVersion()));
402 
403  ThemeInfo *tmpTheme = loadThemeInfo(dtheme);
404  if (tmpTheme)
405  {
406  themeName = tmpTheme->GetName();
407  themesSeen << dirName;
408  m_infoList << dtheme;
409  m_themeStatuses[themeName] = "updateavailable";
410 
411  QFileInfo finfo(tmpTheme->GetPreviewPath());
413  remoteDir.append("/").append(finfo.fileName()),
414  localDir.append("/").append(finfo.fileName()),
415  nullptr);
416  }
417  }
418  }
419  }
420 
421 }
422 
424 {
425  QString curTheme = gCoreContext->GetSetting("Theme");
426  ThemeInfo *themeinfo = nullptr;
427  ThemeInfo *curThemeInfo = nullptr;
428  MythUIButtonListItem *item = nullptr;
429 
430  m_themes->Reset();
431  for (const auto & theme : qAsConst(m_infoList))
432  {
433  if (!m_themeFileNameInfos.contains(theme.filePath()))
434  continue;
435 
436  themeinfo = m_themeFileNameInfos[theme.filePath()];
437  if (!themeinfo)
438  continue;
439 
440  QString buttonText = QString("%1 %2.%3")
441  .arg(themeinfo->GetName())
442  .arg(themeinfo->GetMajorVersion())
443  .arg(themeinfo->GetMinorVersion());
444 
445  item = new MythUIButtonListItem(m_themes, buttonText);
446  if (item)
447  {
448  if (themeinfo->GetDownloadURL().isEmpty())
449  item->DisplayState("local", "themelocation");
450  else
451  item->DisplayState("remote", "themelocation");
452 
453  item->DisplayState(themeinfo->GetAspect(), "aspectstate");
454 
455  item->DisplayState(m_themeStatuses[themeinfo->GetName()],
456  "themestatus");
457  InfoMap infomap;
458  themeinfo->ToMap(infomap);
459  item->SetTextFromMap(infomap);
460  item->SetData(QVariant::fromValue(themeinfo));
461 
462  QString thumbnail = themeinfo->GetPreviewPath();
463  // Downloadable themeinfos have thumbnail copies of their preview images
464  if (!themeinfo->GetDownloadURL().isEmpty())
465  thumbnail = thumbnail.append(".thumb.jpg");
466  item->SetImage(thumbnail);
467 
468  if (curTheme == themeinfo->GetDirectoryName())
469  curThemeInfo = themeinfo;
470  }
471  }
472 
474 
475  if (curThemeInfo)
476  m_themes->SetValueByData(QVariant::fromValue(curThemeInfo));
477 
479  if (current)
481 
482  QString testFile = m_userThemeDir + "/.test";
483  QFile test(testFile);
484  if (test.open(QIODevice::WriteOnly))
485  test.remove();
486  else
487  {
488  ShowOkPopup(tr("Error creating test file, %1 themes directory is "
489  "not writable.").arg(m_userThemeDir));
490  }
491 }
492 
493 ThemeInfo *ThemeChooser::loadThemeInfo(const QFileInfo &theme)
494 {
495  if (theme.fileName() == "default" || theme.fileName() == "default-wide")
496  return nullptr;
497 
498  ThemeInfo *themeinfo = nullptr;
499  if (theme.exists()) // local directory vs http:// or remote URL
500  themeinfo = new ThemeInfo(theme.absoluteFilePath());
501  else
502  themeinfo = new ThemeInfo(theme.filePath());
503 
504  if (!themeinfo)
505  return nullptr;
506 
507  if (themeinfo->GetName().isEmpty() || ((themeinfo->GetType() & THEME_UI) == 0))
508  {
509  delete themeinfo;
510  return nullptr;
511  }
512 
513  m_themeFileNameInfos[theme.filePath()] = themeinfo;
514  m_themeNameInfos[theme.fileName()] = themeinfo;
515 
516  return themeinfo;
517 }
518 
520 {
521  if (m_popupMenu)
522  return;
523 
524  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
525  QString label = tr("Theme Chooser Menu");
526 
527  m_popupMenu =
528  new MythDialogBox(label, popupStack, "themechoosermenupopup");
529 
531 
532  if (m_popupMenu->Create())
533  popupStack->AddScreen(m_popupMenu);
534  else
535  {
536  delete m_popupMenu;
537  m_popupMenu = nullptr;
538  return;
539  }
540 
541  m_popupMenu->SetReturnEvent(this, "popupmenu");
542 
544  {
546  {
547  m_popupMenu->AddButton(tr("Hide Fullscreen Preview"),
549  }
550  else
551  {
552  m_popupMenu->AddButton(tr("Show Fullscreen Preview"),
554  }
555  }
556 
557  m_popupMenu->AddButton(tr("Refresh Downloadable Themes"),
559 
561  if (current)
562  {
563  auto *info = current->GetData().value<ThemeInfo *>();
564 
565  if (info)
566  {
567  m_popupMenu->AddButton(tr("Select Theme"),
568  qOverload<>(&ThemeChooser::saveAndReload));
569 
570  if (info->GetPreviewPath().startsWith(m_userThemeDir))
571  m_popupMenu->AddButton(tr("Delete Theme"),
573  }
574  }
575 
576  if (gCoreContext->GetBoolSetting("ThemeUpdateNofications", true))
577  {
578  m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
580  }
581  else
582  {
583  m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
585  }
586 }
587 
588 void ThemeChooser::popupClosed(const QString& which, int result)
589 {
590  (void)which;
591  (void)result;
592 
593  m_popupMenu = nullptr;
594 }
595 
596 bool ThemeChooser::keyPressEvent(QKeyEvent *event)
597 {
598  if (GetFocusWidget()->keyPressEvent(event))
599  return true;
600 
601  QStringList actions;
602  bool handled = GetMythMainWindow()->TranslateKeyPress("Theme Chooser", event, actions);
603 
604  for (int i = 0; i < actions.size() && !handled; ++i)
605  {
606  QString action = actions[i];
607  handled = true;
608 
609  if (action == "MENU")
610  showPopupMenu();
611  else if (action == "DELETE")
612  removeTheme();
613  else if ((action == "ESCAPE") &&
615  {
617  }
618  else
619  handled = false;
620  }
621 
622  if (!handled && MythScreenType::keyPressEvent(event))
623  handled = true;
624 
625  return handled;
626 }
627 
629 {
631  {
633  {
636 
637  if (m_fullScreenName)
639 
641  m_fullPreviewShowing = false;
642  }
643  else
644  {
646  auto *info = item->GetData().value<ThemeInfo*>();
647  if (info)
648  {
650  {
651  m_fullScreenPreview->SetFilename(info->GetPreviewPath());
653  }
654 
655  if (m_fullScreenName)
656  m_fullScreenName->SetText(info->GetName());
657 
658  m_fullPreviewStateType->DisplayState("fullscreen");
659  m_fullPreviewShowing = true;
660  }
661  }
662  }
663 }
664 
666 {
667  if (gCoreContext->GetBoolSetting("ThemeUpdateNofications", true))
668  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
669  else
670  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
671 }
672 
674 {
675  LOG(VB_GUI, LOG_INFO, LOC + "Forcing remote theme list refresh");
677  gCoreContext->SaveSetting("ThemeInfoDownloadFailures", 0);
679 }
680 
682 {
684  if (current)
686 }
687 
689 {
690  auto *info = item->GetData().value<ThemeInfo *>();
691 
692  if (!info)
693  return;
694 
695  if (!info->GetDownloadURL().isEmpty())
696  {
697  QString testFile = m_userThemeDir + "/.test";
698  QFile test(testFile);
699  if (test.open(QIODevice::WriteOnly))
700  test.remove();
701  else
702  {
703  ShowOkPopup(tr("Unable to install theme, %1 themes directory is "
704  "not writable.").arg(m_userThemeDir));
705  return;
706  }
707 
708  QString downloadURL = info->GetDownloadURL();
709  QFileInfo qfile(downloadURL);
710  QString baseName = qfile.fileName();
711 
712  if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
713  {
714 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
715  QStringList tokens =
716  gCoreContext->GetSetting("ThemeDownloadURL")
717  .split(";", QString::SkipEmptyParts);
718 #else
719  QStringList tokens =
720  gCoreContext->GetSetting("ThemeDownloadURL")
721  .split(";", Qt::SkipEmptyParts);
722 #endif
723  QString origURL = downloadURL;
724  downloadURL.replace(tokens[0], tokens[1]);
725  LOG(VB_FILE, LOG_WARNING, LOC +
726  QString("Theme download URL overridden from %1 to %2.")
727  .arg(origURL, downloadURL));
728  }
729 
730  OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
731  m_downloadTheme = info;
732 #if 0
734  "Temp", baseName);
736 #else
737  QString localFile = GetConfDir() + "/tmp/" + baseName;
738  GetMythDownloadManager()->queueDownload(downloadURL, localFile, this);
739  m_downloadFile = localFile;
741 #endif
742  }
743  else
744  {
745  gCoreContext->SaveSetting("Theme", info->GetDirectoryName());
746  GetMythMainWindow()->JumpTo("Reload Theme");
747  }
748 }
749 
751 {
752  auto *info = item->GetData().value<ThemeInfo*>();
753 
754  if (!info)
755  return;
756 
757  QFileInfo preview(info->GetPreviewPath());
758  InfoMap infomap;
759  info->ToMap(infomap);
760  SetTextFromMap(infomap);
761  if (m_preview)
762  {
763  if (preview.exists())
764  {
765  m_preview->SetFilename(info->GetPreviewPath());
766  m_preview->Load();
767  }
768  else
769  m_preview->Reset();
770  }
772  {
774  {
775  if (preview.exists())
776  {
777  m_fullScreenPreview->SetFilename(info->GetPreviewPath());
779  }
780  else
782  }
783 
784  if (m_fullScreenName)
785  m_fullScreenName->SetText(info->GetName());
786  }
787 
788  MythUIStateType *themeLocation =
789  dynamic_cast<MythUIStateType*>(GetChild("themelocation"));
790  if (themeLocation)
791  {
792  if (info->GetDownloadURL().isEmpty())
793  themeLocation->DisplayState("local");
794  else
795  themeLocation->DisplayState("remote");
796  }
797 
798  MythUIStateType *aspectState =
799  dynamic_cast<MythUIStateType*>(GetChild("aspectstate"));
800  if (aspectState)
801  aspectState->DisplayState(info->GetAspect());
802 }
803 
804 void ThemeChooser::updateProgressBar(int bytesReceived,
805  int bytesTotal)
806 {
807  MythUIProgressBar *progressBar =
808  dynamic_cast<MythUIProgressBar *>(GetChild("downloadprogressbar"));
809 
810  if (!progressBar)
811  return;
812 
813  progressBar->SetUsed(bytesReceived);
814  progressBar->SetTotal(bytesTotal);
815 }
816 
818 {
819  if (e->type() == MythEvent::MythEventMessage)
820  {
821  auto *me = dynamic_cast<MythEvent *>(e);
822  if (me == nullptr)
823  return;
824 
825 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
826  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
827 #else
828  QStringList tokens = me->Message().split(" ", Qt::SkipEmptyParts);
829 #endif
830 
831  if (tokens.isEmpty())
832  return;
833 
834  if (tokens[0] == "DOWNLOAD_FILE")
835  {
836  QStringList args = me->ExtraDataList();
837  if ((m_downloadState == dsIdle) ||
838  (tokens.size() != 2) ||
839  (!m_downloadTheme) ||
840  (args[1] != m_downloadFile))
841  return;
842 
843  if (tokens[1] == "UPDATE")
844  {
845  updateProgressBar(args[2].toInt(), args[3].toInt());
846  }
847  else if (tokens[1] == "FINISHED")
848  {
849  bool remoteFileIsLocal = false;
850  int fileSize = args[2].toInt();
851  int errorCode = args[4].toInt();
852 
853  CloseBusyPopup();
854 
855  QFileInfo file(m_downloadFile);
857  (m_downloadFile.startsWith("myth://")))
858  {
859  // The backend download is finished so start the
860  // frontend download
861  if ((errorCode == 0) &&
862  (fileSize > 0))
863  {
865  QString localFile = GetConfDir() + "/tmp/" +
866  file.fileName();
867  file.setFile(localFile);
868 
869  if (file.exists())
870  {
871  remoteFileIsLocal = true;
872  m_downloadFile = localFile;
873  }
874  else
875  {
877  m_downloadFile, localFile, this);
878  OpenBusyPopup(tr("Copying %1 Theme Package")
879  .arg(m_downloadTheme->GetName()));
880  m_downloadFile = localFile;
881  return;
882  }
883  }
884  else
885  {
887  ShowOkPopup(tr("ERROR downloading theme package on master backend."));
888  }
889  }
890 
892  (file.exists()))
893  {
894  // The frontend download is finished
895  if ((errorCode == 0) &&
896  (fileSize > 0))
897  {
899  auto *extractThread =
903  extractThread, "ThemeExtract");
904 
905  if (!remoteFileIsLocal)
907 
908  OpenBusyPopup(tr("Installing %1 Theme")
909  .arg(m_downloadTheme->GetName()));
910  }
911  else
912  {
914  ShowOkPopup(tr("ERROR downloading theme package from master backend."));
915  }
916  }
917  }
918  }
919  else if ((me->Message() == "THEME_INSTALLED") &&
920  (m_downloadTheme) &&
922  {
924  CloseBusyPopup();
925  QStringList args = me->ExtraDataList();
926 
927  if (!args.isEmpty() && !args[0].isEmpty())
928  QFile::remove(args[0]);
929 
930  QString event = QString("THEME_INSTALLED PATH %1")
931  .arg(m_userThemeDir +
934 
936 
937  // Send a message to ourself so we trigger a reload our next chance
938  auto *me2 = new MythEvent("THEME_RELOAD");
939  qApp->postEvent(this, me2);
940  }
941  else if ((me->Message() == "THEME_RELOAD") &&
942  (m_downloadState == dsIdle))
943  {
944  GetMythMainWindow()->JumpTo("Reload Theme");
945  }
946  }
947 }
948 
950 {
952  if (!current)
953  {
954  ShowOkPopup(tr("Error, no theme selected."));
955  return;
956  }
957 
958  auto *info = current->GetData().value<ThemeInfo *>();
959  if (!info)
960  {
961  ShowOkPopup(tr("Error, unable to find current theme."));
962  return;
963  }
964 
965  if (!info->GetPreviewPath().startsWith(m_userThemeDir))
966  {
967  ShowOkPopup(tr("%1 is not a user-installed theme and can not "
968  "be deleted.").arg(info->GetName()));
969  return;
970  }
971 
972  removeThemeDir(m_userThemeDir + info->GetDirectoryName());
973 
975 }
976 
977 bool ThemeChooser::removeThemeDir(const QString &dirname)
978 {
979  if ((!dirname.startsWith(m_userThemeDir)) &&
980  (!dirname.startsWith(GetMythUI()->GetThemeCacheDir())))
981  return true;
982 
983  QDir dir(dirname);
984 
985  if (!dir.exists())
986  return true;
987 
988  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
989  QFileInfoList list = dir.entryInfoList();
990 
991  for (const auto & fi : qAsConst(list))
992  {
993  if (fi.isFile() && !fi.isSymLink())
994  {
995  if (!QFile::remove(fi.absoluteFilePath()))
996  return false;
997  }
998  else if (fi.isDir() && !fi.isSymLink())
999  {
1000  if (!removeThemeDir(fi.absoluteFilePath()))
1001  return false;
1002  }
1003  }
1004 
1005  return dir.rmdir(dirname);
1006 }
1007 
1009 
1011  m_updateTimer(new QTimer(this))
1012 {
1013  QString version = GetMythSourcePath();
1014 
1015  if (!version.isEmpty() && !version.startsWith("fixes/"))
1016  {
1017  // Treat devel branches as master
1018  m_mythVersions << "trunk";
1019  }
1020  else
1021  {
1022  version = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
1023  version.remove(kVersionDateRE);
1024 
1025  // If a version of the theme for this tag exists, use it...
1026  static const QRegularExpression subexp
1027  { "v[0-9]+\\.([0-9]+)-*", QRegularExpression::CaseInsensitiveOption };
1028  auto match = subexp.match(GetMythSourceVersion());
1029  if (match.hasMatch())
1030  {
1031  for (int idx = match.capturedView(1).toInt(); idx > 0; --idx)
1032  m_mythVersions << version + "." + QString::number(idx);
1033  }
1035  }
1036 
1039  "remotethemes/themes.zip",
1040  "Temp");
1041 
1042  gCoreContext->SaveSetting("ThemeUpdateStatus", "");
1043 
1045 
1046  if (qEnvironmentVariableIsSet("MYTHTV_DEBUGMDM"))
1047  {
1048  LOG(VB_GENERAL, LOG_INFO, "Checking for theme updates every minute");
1049  m_updateTimer->start(1min);
1050  }
1051  else
1052  {
1053  LOG(VB_GENERAL, LOG_INFO, "Checking for theme updates every hour");
1054  m_updateTimer->start(1h);
1055  }
1056 
1057  // Run once 15 seconds from now
1058  QTimer::singleShot(15s, this, &ThemeUpdateChecker::checkForUpdate);
1059 }
1060 
1062 {
1063  if (m_updateTimer)
1064  {
1065  m_updateTimer->stop();
1066  delete m_updateTimer;
1067  m_updateTimer = nullptr;
1068  }
1069 }
1070 
1072 {
1073  if (GetMythUI()->GetCurrentLocation(false, true) != "mainmenu")
1074  return;
1075 
1076  ThemeInfo *localTheme = nullptr;
1077 
1079  {
1080  QStringList::iterator Iversion;
1081 
1082  for (Iversion = m_mythVersions.begin();
1083  Iversion != m_mythVersions.end(); ++Iversion)
1084  {
1085 
1086  QString remoteThemeDir =
1089  QString("remotethemes/%1/%2")
1090  .arg(*Iversion,
1091  GetMythUI()->GetThemeName()),
1092  "Temp");
1093 
1094  QString infoXML = remoteThemeDir;
1095  infoXML.append("/themeinfo.xml");
1096 
1097  LOG(VB_GUI, LOG_INFO, QString("ThemeUpdateChecker Loading '%1'")
1098  .arg(infoXML));
1099 
1100  if (RemoteFile::Exists(infoXML))
1101  {
1102  int locMaj = 0;
1103  int locMin = 0;
1104 
1105  auto *remoteTheme = new ThemeInfo(remoteThemeDir);
1106  if (!remoteTheme || remoteTheme->GetType() & THEME_UNKN)
1107  {
1108  LOG(VB_GENERAL, LOG_ERR,
1109  QString("ThemeUpdateChecker::checkForUpdate(): "
1110  "Unable to create ThemeInfo for %1")
1111  .arg(infoXML));
1112  delete remoteTheme;
1113  remoteTheme = nullptr;
1114  return;
1115  }
1116 
1117  if (!localTheme)
1118  {
1119  localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
1120  if (!localTheme || localTheme->GetType() & THEME_UNKN)
1121  {
1122  LOG(VB_GENERAL, LOG_ERR,
1123  "ThemeUpdateChecker::checkForUpdate(): "
1124  "Unable to create ThemeInfo for current theme");
1125  delete localTheme;
1126  localTheme = nullptr;
1127  return;
1128  }
1129  locMaj = localTheme->GetMajorVersion();
1130  locMin = localTheme->GetMinorVersion();
1131  }
1132 
1133  int rmtMaj = remoteTheme->GetMajorVersion();
1134  int rmtMin = remoteTheme->GetMinorVersion();
1135 
1136  delete remoteTheme;
1137  remoteTheme = nullptr;
1138 
1139  if ((rmtMaj > locMaj) ||
1140  ((rmtMaj == locMaj) &&
1141  (rmtMin > locMin)))
1142  {
1144  QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
1145  .arg(rmtMaj).arg(rmtMin);
1146 
1147  QString status = gCoreContext->GetSetting
1148  ("ThemeUpdateStatus");
1149  QString currentLocation = GetMythUI()->GetCurrentLocation
1150  (false, true);
1151 
1152  if ((!status.startsWith(m_lastKnownThemeVersion)) &&
1153  (currentLocation == "mainmenu"))
1154  {
1155  m_currentVersion = QString("%1.%2")
1156  .arg(locMaj).arg(locMin);
1157  m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);
1158 
1159  gCoreContext->SaveSetting("ThemeUpdateStatus",
1161  + " notified");
1162 
1163  QString message = tr("Version %1 of the %2 theme is now "
1164  "available in the Theme Chooser. "
1165  "The currently installed version "
1166  "is %3.")
1167  .arg(m_newVersion,
1168  GetMythUI()->GetThemeName(),
1170 
1171  ShowOkPopup(message);
1172  break;
1173  }
1174  }
1175  }
1176  }
1177  }
1178 
1179  delete localTheme;
1180 }
1181 
1182 /* 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:286
build_compdb.args
args
Definition: build_compdb.py:11
MythDialogBox::SetReturnEvent
void SetReturnEvent(QObject *retobject, const QString &resultid)
Definition: mythdialogbox.cpp:301
extractZIP
bool extractZIP(QString zipFile, const QString &outDir)
Definition: unziputil.cpp:13
MythEvent::MythEventMessage
static Type MythEventMessage
Definition: mythevent.h:79
MythUIButtonList::GetItemCurrent
MythUIButtonListItem * GetItemCurrent() const
Definition: mythuibuttonlist.cpp:1587
MythScreenType::SetBusyPopupMessage
void SetBusyPopupMessage(const QString &message)
Definition: mythscreentype.cpp:345
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:807
ThemeUpdateChecker::m_lastKnownThemeVersion
QString m_lastKnownThemeVersion
Definition: themechooser.h:113
MythUIButtonList::SetValueByData
void SetValueByData(const QVariant &data)
Definition: mythuibuttonlist.cpp:1539
ThemeChooser
View and select installed themes.
Definition: themechooser.h:27
mythuitext.h
ThemeChooser::m_themes
MythUIButtonList * m_themes
Definition: themechooser.h:74
mythuiprogressbar.h
kVersionDateRE
static const QRegularExpression kVersionDateRE
Definition: themechooser.cpp:44
unziputil.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:82
ThemeExtractThread::run
void run() override
Definition: themechooser.cpp:58
MythUIButtonListItem::DisplayState
void DisplayState(const QString &state, const QString &name)
Definition: mythuibuttonlist.cpp:3563
ThemeInfo::GetAspect
QString GetAspect() const
Definition: themeinfo.h:27
RemoteFile::Exists
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:454
MythUIType::GetChild
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:133
mythscreenstack.h
MythMainWindow::JumpTo
void JumpTo(const QString &Destination, bool Pop=true)
Definition: mythmainwindow.cpp:1450
MythUIImage::Load
bool Load(bool allowLoadInBackground=true, bool forceStat=false)
Load the image(s), wraps ImageLoader::LoadImage()
Definition: mythuiimage.cpp:969
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:817
GetMythSourceVersion
const char * GetMythSourceVersion()
Definition: mythversion.cpp:5
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:69
ThemeInfo::GetMinorVersion
int GetMinorVersion() const
Definition: themeinfo.h:36
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:673
ThemeChooser::m_refreshDownloadableThemes
bool m_refreshDownloadableThemes
Definition: themechooser.h:83
MythScreenType::OpenBusyPopup
void OpenBusyPopup(const QString &message="")
Definition: mythscreentype.cpp:320
MythUIGroup
Create a group of widgets.
Definition: mythuigroup.h:11
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythDialogBox::Closed
void Closed(QString, int)
MythScreenType
Screen in which all other widgets are contained and rendered.
Definition: mythscreentype.h:45
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:588
MythUIImage::Reset
void Reset(void) override
Reset the image back to the default defined in the theme.
Definition: mythuiimage.cpp:646
ThemeUpdateChecker::m_updateTimer
QTimer * m_updateTimer
Definition: themechooser.h:110
ThemeChooser::updateProgressBar
void updateProgressBar(int bytesReceived, int bytesTotal)
Definition: themechooser.cpp:804
ThemeInfo::GetDownloadURL
QString GetDownloadURL() const
Definition: themeinfo.h:38
THEME_UNKN
@ THEME_UNKN
Definition: themeinfo.h:14
mythuibuttonlist.h
mythuiimage.h
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:14
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:113
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:351
MythUIButtonListItem
Definition: mythuibuttonlist.h:41
ThemeInfo::GetName
QString GetName() const
Definition: themeinfo.h:29
ThemeChooser::showPopupMenu
void showPopupMenu(void)
Definition: themechooser.cpp:519
MythScreenType::ReloadInBackground
void ReloadInBackground(void)
Definition: mythscreentype.cpp:314
mythdate.h
MythUIProgressBar::SetTotal
void SetTotal(int value)
Definition: mythuiprogressbar.cpp:81
mythlogging.h
MythCoreContext::GenMythURL
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
Definition: mythcorecontext.cpp:760
GetConfDir
QString GetConfDir(void)
Definition: mythdirs.cpp:224
ThemeExtractThread::m_destDir
QString m_destDir
Definition: themechooser.cpp:69
LOC
#define LOC
Definition: themechooser.cpp:40
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:1104
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:1544
MythScreenType::SetFocusWidget
bool SetFocusWidget(MythUIType *widget=nullptr)
Definition: mythscreentype.cpp:118
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:493
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:206
MythUIComposite::SetTextFromMap
virtual void SetTextFromMap(const InfoMap &infoMap)
Definition: mythuicomposite.cpp:9
ThemeUpdateChecker::checkForUpdate
void checkForUpdate(void)
Definition: themechooser.cpp:1071
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:33
StorageGroup::GetFirstDir
QString GetFirstDir(bool appendSlash=false) const
Definition: storagegroup.cpp:189
ThemeInfo
Definition: themeinfo.h:20
storagegroup.h
ThemeChooser::dsExtractingTheme
@ dsExtractingTheme
Definition: themechooser.h:66
ThemeChooser::saveAndReload
void saveAndReload(void)
Definition: themechooser.cpp:681
RemoteFile::DeleteFile
static bool DeleteFile(const QString &url)
Definition: remotefile.cpp:411
MythUIButtonListItem::GetData
QVariant GetData()
Definition: mythuibuttonlist.cpp:3665
MYTH_BINARY_VERSION
static constexpr const char * MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:15
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:55
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:912
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
GetMythSourcePath
const char * GetMythSourcePath()
Definition: mythversion.cpp:10
MythUIButtonListItem::SetTextFromMap
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
Definition: mythuibuttonlist.cpp:3285
MythCoreContext::GetBoolSetting
bool GetBoolSetting(const QString &key, bool defaultval=false)
Definition: mythcorecontext.cpp:906
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:1061
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:980
ThemeChooser::toggleFullscreenPreview
void toggleFullscreenPreview(void)
Definition: themechooser.cpp:628
ThemeUpdateChecker::ThemeUpdateChecker
ThemeUpdateChecker(void)
Definition: themechooser.cpp:1010
MythUIText
All purpose text widget, displays a text string.
Definition: mythuitext.h:28
MythScreenType::keyPressEvent
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
Definition: mythscreentype.cpp:404
ThemeInfo::GetType
int GetType() const
Definition: themeinfo.h:34
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:228
XMLParseBase::LoadWindowFromXML
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
Definition: xmlparsebase.cpp:695
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:3429
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:132
ThemeChooser::m_fullScreenName
MythUIText * m_fullScreenName
Definition: themechooser.h:79
ThemeInfo::GetMajorVersion
int GetMajorVersion() const
Definition: themeinfo.h:35
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:116
GetMythMainWindow
MythMainWindow * GetMythMainWindow(void)
Definition: mythmainwindow.cpp:102
StorageGroup
Definition: storagegroup.h:11
build_compdb.action
action
Definition: build_compdb.py:9
ThemeChooser::toggleThemeUpdateNotifications
static void toggleThemeUpdateNotifications(void)
Definition: themechooser.cpp:665
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:596
MythMainWindow::GetStack
MythScreenStack * GetStack(const QString &Stackname)
Definition: mythmainwindow.cpp:320
ThemeChooser::~ThemeChooser
~ThemeChooser() override
Definition: themechooser.cpp:87
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:838
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:881
downloadURL
static bool downloadURL(const QString &url, QByteArray *buffer, QString &finalURL)
Definition: httplivestreambuffer.cpp:94
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:677
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:423
ThemeChooser::dsIdle
@ dsIdle
Definition: themechooser.h:63
MythUIButtonListItem::SetData
void SetData(QVariant data)
Definition: mythuibuttonlist.cpp:3660
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:563
nv_python_libs.bbciplayer.bbciplayer_api.version
string version
Definition: bbciplayer_api.py:77
THEME_UI
@ THEME_UI
Definition: themeinfo.h:15
MythCoreContext::SaveSettingOnHost
bool SaveSettingOnHost(const QString &key, const QString &newValue, const QString &host)
Definition: mythcorecontext.cpp:891
ThemeChooser::removeTheme
void removeTheme(void)
Definition: themechooser.cpp:949
RemoteDownloadFile
QString RemoteDownloadFile(const QString &url, const QString &storageGroup, const QString &filename)
Definition: mythcoreutil.cpp:91
ThemeInfo::GetDirectoryName
QString GetDirectoryName() const
Definition: themeinfo.h:42
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:338
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:977
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:898
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145
ThemeChooser::itemChanged
void itemChanged(MythUIButtonListItem *item)
Definition: themechooser.cpp:750