MythTV  0.28pre
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Groups Pages
themechooser.cpp
Go to the documentation of this file.
1 
2 // Theme Chooser headers
3 #include "themechooser.h"
4 
5 // Qt headers
6 #include <QCoreApplication>
7 #include <QRunnable>
8 #include <QRegExp>
9 
10 // MythTV headers
11 #include "mythcorecontext.h"
12 #include "mythcoreutil.h"
13 #include "mthreadpool.h"
14 #include "remotefile.h"
15 #include "mythdownloadmanager.h"
16 #include "programtypes.h"
17 #include "mythsystemevent.h"
18 #include "mythdate.h"
19 #include "mythversion.h"
20 #include "mythlogging.h"
21 #include "storagegroup.h"
22 
23 // LibMythUI headers
24 #include "mythmainwindow.h"
25 #include "mythuihelper.h"
26 #include "mythuiprogressbar.h"
27 #include "mythdialogbox.h"
28 #include "mythuibuttonlist.h"
29 #include "mythscreenstack.h"
30 #include "mythuistatetype.h"
31 
32 #define LOC QString("ThemeChooser: ")
33 #define LOC_WARN QString("ThemeChooser, Warning: ")
34 #define LOC_ERR QString("ThemeChooser, Error: ")
35 
39 class ThemeExtractThread : public QRunnable
40 {
41  public:
43  const QString &srcFile, const QString &destDir) :
44  m_parent(parent),
45  m_srcFile(srcFile),
46  m_destDir(destDir)
47  {
48  m_srcFile.detach();
49  m_destDir.detach();
50  }
51 
52  void run()
53  {
55 
56  MythEvent *me =
57  new MythEvent("THEME_INSTALLED", QStringList(m_srcFile));
58  QCoreApplication::postEvent(m_parent, me);
59  }
60 
61  private:
63  QString m_srcFile;
64  QString m_destDir;
65 };
66 
67 
73  const QString &name) :
74  MythScreenType(parent, name),
75  m_themes(NULL),
76  m_preview(NULL),
77  m_fullPreviewShowing(false),
78  m_fullPreviewStateType(NULL),
79  m_fullScreenName(NULL),
80  m_fullScreenPreview(NULL),
81  m_refreshDownloadableThemes(false),
82  m_downloadTheme(NULL),
83  m_downloadState(dsIdle),
84  m_popupMenu(NULL)
85 {
87 
88  StorageGroup sgroup("Themes", gCoreContext->GetHostName());
89  m_userThemeDir = sgroup.GetFirstDir(true);
90 }
91 
93 {
95 }
96 
97 static bool sortThemeNames(const QFileInfo &s1, const QFileInfo &s2)
98 {
99  return s1.fileName().toLower() < s2.fileName().toLower();
100 }
101 
102 
104 {
105  // Load the theme for this screen
106  if (!LoadWindowFromXML("settings-ui.xml", "themechooser", this))
107  return false;
108 
109  bool err = false;
110  UIUtilE::Assign(this, m_themes, "themes", &err);
111 
112  UIUtilW::Assign(this, m_preview, "preview");
113  UIUtilW::Assign(this, m_fullPreviewStateType, "fullpreviewstate");
114 
116  {
117  MythUIGroup *state =
118  dynamic_cast<MythUIGroup*>
119  (m_fullPreviewStateType->GetChild("fullscreen"));
120  if (state)
121  {
123  dynamic_cast<MythUIText*>(state->GetChild("fullscreenname"));
125  dynamic_cast<MythUIImage*>(state->GetChild("fullscreenpreview"));
126  }
127  }
128 
129  if (err)
130  {
131  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load screen 'themechooser'");
132  return false;
133  }
134 
135  connect(m_themes, SIGNAL(itemClicked(MythUIButtonListItem*)),
136  this, SLOT(saveAndReload(MythUIButtonListItem*)));
137  connect(m_themes, SIGNAL(itemSelected(MythUIButtonListItem*)),
138  this, SLOT(itemChanged(MythUIButtonListItem*)));
139 
140  BuildFocusList();
141 
143 
144  return true;
145 }
146 
148 {
149  SetBusyPopupMessage(tr("Loading Installed Themes"));
150 
151  QString MythVersion = MYTH_SOURCE_PATH;
152  QStringList themesSeen;
153  QDir themes(m_userThemeDir);
154  themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
155  themes.setSorting(QDir::Name | QDir::IgnoreCase);
156 
157  // Treat devel branches as master
158  if (MythVersion.startsWith("devel/"))
159  MythVersion = "master";
160 
161  // FIXME: For now, treat git master the same as svn trunk
162  if (MythVersion == "master")
163  MythVersion = "trunk";
164 
165  if (MythVersion != "trunk")
166  {
167  MythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
168  MythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
169  }
170 
171  m_infoList = themes.entryInfoList();
172 
173  for( QFileInfoList::iterator it = m_infoList.begin();
174  it != m_infoList.end();
175  ++it )
176  {
177  if (loadThemeInfo(*it))
178  {
179  themesSeen << (*it).fileName();
180  m_themeStatuses[(*it).fileName()] = "default";
181  }
182  }
183 
184  themes.setPath(GetThemesParentDir());
185  QFileInfoList sharedThemes = themes.entryInfoList();
186  for( QFileInfoList::iterator it = sharedThemes.begin();
187  it != sharedThemes.end();
188  ++it )
189  {
190  if ((!themesSeen.contains((*it).fileName())) &&
191  (loadThemeInfo(*it)))
192  {
193  m_infoList << *it;
194  themesSeen << (*it).fileName();
195  m_themeStatuses[(*it).fileName()] = "default";
196  }
197  }
198 
199  QString remoteThemesFile = GetConfDir();
200  remoteThemesFile.append("/tmp/themes.zip");
201  QString themeSite = QString("%1/%2")
202  .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
203  "http://themes.mythtv.org/themes/repository")).arg(MythVersion);
204  QDir remoteThemesDir(GetMythUI()->GetThemeCacheDir()
205  .append("/themechooser/").append(MythVersion));
206 
207  int downloadFailures =
208  gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
209  if (QFile::exists(remoteThemesFile))
210  {
211  QFileInfo finfo(remoteThemesFile);
212  if (finfo.lastModified().toUTC() <
213  MythDate::current().addSecs(-600))
214  {
215  LOG(VB_GUI, LOG_INFO, LOC +
216  QString("%1 is over 10 minutes old, forcing "
217  "remote theme list download").arg(remoteThemesFile));
219  }
220 
221  if (!remoteThemesDir.exists())
223  }
224  else if (downloadFailures < 2) // (and themes.zip does not exist)
225  {
226  LOG(VB_GUI, LOG_INFO, LOC +
227  QString("%1 does not exist, forcing remote theme "
228  "list download").arg(remoteThemesFile));
230  }
231 
233  {
234  SetBusyPopupMessage(tr("Refreshing Downloadable Themes Information"));
235 
236  QString url = themeSite;
237  url.append("/themes.zip");
238  QString destdir = GetMythUI()->GetThemeCacheDir();
239  destdir.append("/themechooser");
240  QString versiondir = QString("%1/%2").arg(destdir).arg(MythVersion);
241  removeThemeDir(versiondir);
242  QDir dir;
243  dir.mkpath(destdir);
244  bool result = GetMythDownloadManager()->download(url, remoteThemesFile);
245 
246  SetBusyPopupMessage(tr("Extracting Downloadable Themes Information"));
247 
248  if (!result || !extractZIP(remoteThemesFile, destdir))
249  {
250  QFile::remove(remoteThemesFile);
251 
252  downloadFailures++;
253  gCoreContext->SaveSetting("ThemeInfoDownloadFailures",
254  downloadFailures);
255  }
256  }
257 
258  if ((QFile::exists(remoteThemesFile)) &&
259  (remoteThemesDir.exists()))
260  {
261  SetBusyPopupMessage(tr("Loading Downloadable Themes"));
262 
263  LOG(VB_GUI, LOG_INFO, LOC +
264  QString("%1 and %2 exist, using cached remote themes list")
265  .arg(remoteThemesFile).arg(remoteThemesDir.absolutePath()));
266 
267  QString themesPath = remoteThemesDir.absolutePath();
268  themes.setPath(themesPath);
269 
270  QFileInfoList downloadableThemes = themes.entryInfoList();
271  for( QFileInfoList::iterator it = downloadableThemes.begin();
272  it != downloadableThemes.end();
273  ++it )
274  {
275  QString dirName = (*it).fileName();
276  QString themeName = dirName;
277  QString remoteDir = themeSite;
278  remoteDir.append("/").append(dirName);
279  QString localDir = themes.absolutePath();
280  localDir.append("/").append(dirName);
281 
282  if (themesSeen.contains(dirName))
283  {
284  ThemeInfo remoteTheme((*it).absoluteFilePath());
285  ThemeInfo *localTheme = m_themeNameInfos[dirName];
286 
287  themeName = remoteTheme.GetName();
288 
289  int rmtMaj = remoteTheme.GetMajorVersion();
290  int rmtMin = remoteTheme.GetMinorVersion();
291  int locMaj = localTheme->GetMajorVersion();
292  int locMin = localTheme->GetMinorVersion();
293 
294  if ((rmtMaj > locMaj) ||
295  ((rmtMaj == locMaj) &&
296  (rmtMin > locMin)))
297  {
298  if (loadThemeInfo(*it))
299  {
300  m_infoList << *it;
301  m_themeStatuses[themeName] = "updateavailable";
302 
303  QFileInfo finfo(remoteTheme.GetPreviewPath());
305  remoteDir.append("/").append(finfo.fileName()),
306  localDir.append("/").append(finfo.fileName()),
307  NULL);
308  }
309  }
310  else if ((rmtMaj == locMaj) &&
311  (rmtMin == locMin))
312  {
313  m_themeStatuses[themeName] = "uptodate";
314  }
315  }
316  else
317  {
318  ThemeInfo *remoteTheme = loadThemeInfo(*it);
319  if (remoteTheme)
320  {
321  themeName = remoteTheme->GetName();
322  themesSeen << dirName;
323  m_infoList << *it;
324  m_themeStatuses[themeName] = "updateavailable";
325 
326  QFileInfo finfo(remoteTheme->GetPreviewPath());
328  remoteDir.append("/").append(finfo.fileName()),
329  localDir.append("/").append(finfo.fileName()),
330  NULL);
331  }
332  }
333  }
334  }
335 
336  ResetBusyPopup();
337 
338  qSort(m_infoList.begin(), m_infoList.end(), sortThemeNames);
339 }
340 
342 {
343  QString curTheme = gCoreContext->GetSetting("Theme");
344  ThemeInfo *themeinfo = NULL;
345  ThemeInfo *curThemeInfo = NULL;
346  MythUIButtonListItem *item = NULL;
347 
348  m_themes->Reset();
349  for( QFileInfoList::iterator it = m_infoList.begin();
350  it != m_infoList.end();
351  ++it )
352  {
353  QFileInfo &theme = *it;
354 
355  if (!m_themeFileNameInfos.contains(theme.filePath()))
356  continue;
357 
358  themeinfo = m_themeFileNameInfos[theme.filePath()];
359  if (!themeinfo)
360  continue;
361 
362  QString buttonText = QString("%1 %2.%3")
363  .arg(themeinfo->GetName())
364  .arg(themeinfo->GetMajorVersion())
365  .arg(themeinfo->GetMinorVersion());
366 
367  item = new MythUIButtonListItem(m_themes, buttonText);
368  if (item)
369  {
370  if (themeinfo->GetDownloadURL().isEmpty())
371  item->DisplayState("local", "themelocation");
372  else
373  item->DisplayState("remote", "themelocation");
374 
375  item->DisplayState(themeinfo->GetAspect(), "aspectstate");
376 
377  item->DisplayState(m_themeStatuses[themeinfo->GetName()],
378  "themestatus");
379  InfoMap infomap;
380  themeinfo->ToMap(infomap);
381  item->SetTextFromMap(infomap);
382  item->SetData(qVariantFromValue(themeinfo));
383 
384  QString thumbnail = themeinfo->GetPreviewPath();
385  QFileInfo fInfo(thumbnail);
386  // Downloadable themeinfos have thumbnail copies of their preview images
387  if (!themeinfo->GetDownloadURL().isEmpty())
388  thumbnail = thumbnail.append(".thumb.jpg");
389  item->SetImage(thumbnail);
390 
391  if (curTheme == themeinfo->GetDirectoryName())
392  curThemeInfo = themeinfo;
393  }
394  }
395 
397 
398  if (curThemeInfo)
399  m_themes->SetValueByData(qVariantFromValue(curThemeInfo));
400 
402  if (current)
403  itemChanged(current);
404 
405  QString testFile = m_userThemeDir + "/.test";
406  QFile test(testFile);
407  if (test.open(QIODevice::WriteOnly))
408  test.remove();
409  else
410  {
411  ShowOkPopup(tr("Error creating test file, %1 themes directory is "
412  "not writable.").arg(m_userThemeDir));
413  }
414 }
415 
417 {
418  if (theme.fileName() == "default" || theme.fileName() == "default-wide")
419  return NULL;
420 
421  ThemeInfo *themeinfo = NULL;
422  if (theme.exists()) // local directory vs http:// or remote URL
423  themeinfo = new ThemeInfo(theme.absoluteFilePath());
424  else
425  themeinfo = new ThemeInfo(theme.filePath());
426 
427  if (!themeinfo)
428  return NULL;
429 
430  if (themeinfo->GetName().isEmpty() || !(themeinfo->GetType() & THEME_UI))
431  {
432  delete themeinfo;
433  return NULL;
434  }
435 
436  m_themeFileNameInfos[theme.filePath()] = themeinfo;
437  m_themeNameInfos[theme.fileName()] = themeinfo;
438 
439  return themeinfo;
440 }
441 
443 {
444  if (m_popupMenu)
445  return;
446 
447  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
448  QString label = tr("Theme Chooser Menu");
449 
450  m_popupMenu =
451  new MythDialogBox(label, popupStack, "themechoosermenupopup");
452 
453  connect(m_popupMenu, SIGNAL(Closed(QString, int)), SLOT(popupClosed(QString, int)));
454 
455  if (m_popupMenu->Create())
456  popupStack->AddScreen(m_popupMenu);
457  else
458  {
459  delete m_popupMenu;
460  m_popupMenu = NULL;
461  return;
462  }
463 
464  m_popupMenu->SetReturnEvent(this, "popupmenu");
465 
467  {
469  m_popupMenu->AddButton(tr("Hide Fullscreen Preview"),
470  SLOT(toggleFullscreenPreview()));
471  else
472  m_popupMenu->AddButton(tr("Show Fullscreen Preview"),
473  SLOT(toggleFullscreenPreview()));
474  }
475 
476  m_popupMenu->AddButton(tr("Refresh Downloadable Themes"),
477  SLOT(refreshDownloadableThemes()));
478 
480  if (current)
481  {
482  ThemeInfo *info = current->GetData().value<ThemeInfo *>();
483 
484  if (info)
485  {
486  m_popupMenu->AddButton(tr("Select Theme"),
487  SLOT(saveAndReload()));
488 
489  if (info->GetPreviewPath().startsWith(m_userThemeDir))
490  m_popupMenu->AddButton(tr("Delete Theme"),
491  SLOT(removeTheme()));
492  }
493  }
494 
495  if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
496  m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
498  else
499  m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
501 }
502 
503 void ThemeChooser::popupClosed(QString which, int result)
504 {
505  (void)which;
506  (void)result;
507 
508  m_popupMenu = NULL;
509 }
510 
511 bool ThemeChooser::keyPressEvent(QKeyEvent *event)
512 {
513  if (GetFocusWidget()->keyPressEvent(event))
514  return true;
515 
516  bool handled = false;
517  QStringList actions;
518  handled = GetMythMainWindow()->TranslateKeyPress("Theme Chooser", event, actions);
519 
520  for (int i = 0; i < actions.size() && !handled; ++i)
521  {
522  QString action = actions[i];
523  handled = true;
524 
525  if (action == "MENU")
526  showPopupMenu();
527  else if (action == "DELETE")
528  removeTheme();
529  else if ((action == "ESCAPE") &&
531  {
533  }
534  else
535  handled = false;
536  }
537 
538  if (!handled && MythScreenType::keyPressEvent(event))
539  handled = true;
540 
541  return handled;
542 }
543 
545 {
547  {
549  {
552 
553  if (m_fullScreenName)
555 
557  m_fullPreviewShowing = false;
558  }
559  else
560  {
562  ThemeInfo *info = item->GetData().value<ThemeInfo*>();
563  if (info)
564  {
566  {
569  }
570 
571  if (m_fullScreenName)
572  m_fullScreenName->SetText(info->GetName());
573 
574  m_fullPreviewStateType->DisplayState("fullscreen");
575  m_fullPreviewShowing = true;
576  }
577  }
578  }
579 }
580 
582 {
583  if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
584  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
585  else
586  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
587 }
588 
590 {
591  LOG(VB_GUI, LOG_INFO, LOC + "Forcing remote theme list refresh");
593  gCoreContext->SaveSetting("ThemeInfoDownloadFailures", 0);
595 }
596 
598 {
600  if (current)
601  saveAndReload(current);
602 }
603 
605 {
606  ThemeInfo *info = item->GetData().value<ThemeInfo *>();
607 
608  if (!info)
609  return;
610 
611  if (!info->GetDownloadURL().isEmpty())
612  {
613  QString testFile = m_userThemeDir + "/.test";
614  QFile test(testFile);
615  if (test.open(QIODevice::WriteOnly))
616  test.remove();
617  else
618  {
619  ShowOkPopup(tr("Unable to install theme, %1 themes directory is "
620  "not writable.").arg(m_userThemeDir));
621  return;
622  }
623 
624  QString downloadURL = info->GetDownloadURL();
625  QFileInfo qfile(downloadURL);
626  QString baseName = qfile.fileName();
627 
628  if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
629  {
630  QStringList tokens =
631  gCoreContext->GetSetting("ThemeDownloadURL")
632  .split(";", QString::SkipEmptyParts);
633  QString origURL = downloadURL;
634  downloadURL.replace(tokens[0], tokens[1]);
635  LOG(VB_FILE, LOG_WARNING, LOC +
636  QString("Theme download URL overridden from %1 to %2.")
637  .arg(origURL).arg(downloadURL));
638  }
639 
640  OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
641  m_downloadTheme = info;
642 #if 0
643  m_downloadFile = RemoteDownloadFile(downloadURL,
644  "Temp", baseName);
646 #else
647  QString localFile = GetConfDir() + "/tmp/" + baseName;
648  GetMythDownloadManager()->queueDownload(downloadURL, localFile, this);
649  m_downloadFile = localFile;
651 #endif
652  }
653  else
654  {
655  gCoreContext->SaveSetting("Theme", info->GetDirectoryName());
656  GetMythMainWindow()->JumpTo("Reload Theme");
657  }
658 }
659 
661 {
662  ThemeInfo *info = item->GetData().value<ThemeInfo*>();
663 
664  if (!info)
665  return;
666 
667  QFileInfo preview(info->GetPreviewPath());
668  InfoMap infomap;
669  info->ToMap(infomap);
670  SetTextFromMap(infomap);
671  if (m_preview)
672  {
673  if (preview.exists())
674  {
676  m_preview->Load();
677  }
678  else
679  m_preview->Reset();
680  }
682  {
684  {
685  if (preview.exists())
686  {
689  }
690  else
692  }
693 
694  if (m_fullScreenName)
695  m_fullScreenName->SetText(info->GetName());
696  }
697 
698  MythUIStateType *themeLocation =
699  dynamic_cast<MythUIStateType*>(GetChild("themelocation"));
700  if (themeLocation)
701  {
702  if (info->GetDownloadURL().isEmpty())
703  themeLocation->DisplayState("local");
704  else
705  themeLocation->DisplayState("remote");
706  }
707 
708  MythUIStateType *aspectState =
709  dynamic_cast<MythUIStateType*>(GetChild("aspectstate"));
710  if (aspectState)
711  aspectState->DisplayState(info->GetAspect());
712 }
713 
714 void ThemeChooser::updateProgressBar(int bytesReceived,
715  int bytesTotal)
716 {
717  MythUIProgressBar *progressBar =
718  dynamic_cast<MythUIProgressBar *>(GetChild("downloadprogressbar"));
719 
720  if (!progressBar)
721  return;
722 
723  progressBar->SetUsed(bytesReceived);
724  progressBar->SetTotal(bytesTotal);
725 }
726 
728 {
729  if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
730  {
731  MythEvent *me = (MythEvent *)e;
732  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
733 
734  if (tokens.isEmpty())
735  return;
736 
737  if (tokens[0] == "DOWNLOAD_FILE")
738  {
739  QStringList args = me->ExtraDataList();
740  if ((m_downloadState == dsIdle) ||
741  (tokens.size() != 2) ||
742  (!m_downloadTheme) ||
743  (args[1] != m_downloadFile))
744  return;
745 
746  if (tokens[1] == "UPDATE")
747  {
748  updateProgressBar(args[2].toInt(), args[3].toInt());
749  }
750  else if (tokens[1] == "FINISHED")
751  {
752  bool remoteFileIsLocal = false;
753  int fileSize = args[2].toInt();
754  int errorCode = args[4].toInt();
755 
756  CloseBusyPopup();
757 
758  QFileInfo file(m_downloadFile);
760  (m_downloadFile.startsWith("myth://")))
761  {
762  // The backend download is finished so start the
763  // frontend download
764  if ((errorCode == 0) &&
765  (fileSize > 0))
766  {
768  QString localFile = GetConfDir() + "/tmp/" +
769  file.fileName();
770  file.setFile(localFile);
771 
772  if (file.exists())
773  {
774  remoteFileIsLocal = true;
775  m_downloadFile = localFile;
776  }
777  else
778  {
780  m_downloadFile, localFile, this);
781  OpenBusyPopup(tr("Copying %1 Theme Package")
782  .arg(m_downloadTheme->GetName()));
783  m_downloadFile = localFile;
784  return;
785  }
786  }
787  else
788  {
790  ShowOkPopup(tr("ERROR downloading theme package on master backend."));
791  }
792  }
793 
795  (file.exists()))
796  {
797  // The frontend download is finished
798  if ((errorCode == 0) &&
799  (fileSize > 0))
800  {
802  ThemeExtractThread *extractThread =
806  extractThread, "ThemeExtract");
807 
808  if (!remoteFileIsLocal)
809  RemoteFile::DeleteFile(args[0]);
810 
811  OpenBusyPopup(tr("Installing %1 Theme")
812  .arg(m_downloadTheme->GetName()));
813  }
814  else
815  {
817  ShowOkPopup(tr("ERROR downloading theme package from master backend."));
818  }
819  }
820  }
821  }
822  else if ((me->Message() == "THEME_INSTALLED") &&
823  (m_downloadTheme) &&
825  {
827  CloseBusyPopup();
828  QStringList args = me->ExtraDataList();
829  QFile::remove(args[0]);
830 
831  QString event = QString("THEME_INSTALLED PATH %1")
832  .arg(m_userThemeDir +
835 
837 
838  // Send a message to ourself so we trigger a reload our next chance
839  MythEvent *me = new MythEvent("THEME_RELOAD");
840  qApp->postEvent(this, me);
841  }
842  else if ((me->Message() == "THEME_RELOAD") &&
843  (m_downloadState == dsIdle))
844  {
845  GetMythMainWindow()->JumpTo("Reload Theme");
846  }
847  }
848 }
849 
851 {
853  if (!current)
854  {
855  ShowOkPopup(tr("Error, no theme selected."));
856  return;
857  }
858 
859  ThemeInfo *info = current->GetData().value<ThemeInfo *>();
860  if (!info)
861  {
862  ShowOkPopup(tr("Error, unable to find current theme."));
863  return;
864  }
865 
866  if (!info->GetPreviewPath().startsWith(m_userThemeDir))
867  {
868  ShowOkPopup(tr("%1 is not a user-installed theme and can not "
869  "be deleted.").arg(info->GetName()));
870  return;
871  }
872 
874 
876 }
877 
879 {
880  if ((!dirname.startsWith(m_userThemeDir)) &&
881  (!dirname.startsWith(GetMythUI()->GetThemeCacheDir())))
882  return;
883 
884  QDir dir(dirname);
885 
886  if (!dir.exists())
887  return;
888 
889  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
890  QFileInfoList list = dir.entryInfoList();
891  QFileInfoList::const_iterator it = list.begin();
892  const QFileInfo *fi;
893 
894  while (it != list.end())
895  {
896  fi = &(*it++);
897  if (fi->isFile() && !fi->isSymLink())
898  {
899  QFile::remove(fi->absoluteFilePath());
900  }
901  else if (fi->isDir() && !fi->isSymLink())
902  {
903  removeThemeDir(fi->absoluteFilePath());
904  }
905  }
906 
907  dir.rmdir(dirname);
908 }
909 
911 
913  m_updateTimer(new QTimer(this))
914 {
915  m_mythVersion = MYTH_SOURCE_PATH;
916 
917  // Treat devel branches as master
918  if (m_mythVersion.startsWith("devel/"))
919  m_mythVersion = "master";
920 
921  // FIXME: For now, treat git master the same as svn trunk
922  if (m_mythVersion == "master")
923  m_mythVersion = "trunk";
924 
925  if (m_mythVersion != "trunk")
926  {
927  m_mythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
928  m_mythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
929  }
930 
932  0,
933  "remotethemes/themes.zip",
934  "Temp");
935 
936  gCoreContext->SaveSetting("ThemeUpdateStatus", "");
937 
938  connect(m_updateTimer, SIGNAL(timeout()), SLOT(checkForUpdate()));
939  m_updateTimer->start(60 * 60 * 1000); // Run once an hour
940 
941  // Run once 15 seconds from now
942  QTimer::singleShot(15 * 1000, this, SLOT(checkForUpdate()));
943 }
944 
946 {
947  if (m_updateTimer)
948  {
949  m_updateTimer->stop();
950  delete m_updateTimer;
951  m_updateTimer = NULL;
952  }
953 }
954 
956 {
957  if (GetMythUI()->GetCurrentLocation(false, true) != "mainmenu")
958  return;
959 
961  {
962  QString remoteThemeDir =
963  gCoreContext->GenMythURL(gCoreContext->GetSetting("MasterServerIP"),
964  0,
965  QString("remotethemes/%1/%2")
966  .arg(m_mythVersion)
967  .arg(GetMythUI()->GetThemeName()),
968  "Temp");
969 
970  QString infoXML = remoteThemeDir;
971  infoXML.append("/themeinfo.xml");
972 
973  if (RemoteFile::Exists(infoXML))
974  {
975  ThemeInfo *remoteTheme = new ThemeInfo(remoteThemeDir);
976  if (!remoteTheme)
977  {
978  LOG(VB_GENERAL, LOG_ERR,
979  QString("ThemeUpdateChecker::checkForUpdate(): "
980  "Unable to create ThemeInfo for %1")
981  .arg(infoXML));
982  return;
983  }
984 
985  ThemeInfo *localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
986  if (!localTheme)
987  {
988  LOG(VB_GENERAL, LOG_ERR,
989  "ThemeUpdateChecker::checkForUpdate(): "
990  "Unable to create ThemeInfo for current theme");
991  return;
992  }
993 
994  int rmtMaj = remoteTheme->GetMajorVersion();
995  int rmtMin = remoteTheme->GetMinorVersion();
996  int locMaj = localTheme->GetMajorVersion();
997  int locMin = localTheme->GetMinorVersion();
998 
999  if ((rmtMaj > locMaj) ||
1000  ((rmtMaj == locMaj) &&
1001  (rmtMin > locMin)))
1002  {
1004  QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
1005  .arg(rmtMaj).arg(rmtMin);
1006 
1007  QString status = gCoreContext->GetSetting("ThemeUpdateStatus");
1008  QString currentLocation = GetMythUI()->GetCurrentLocation(false, true);
1009 
1010  if ((!status.startsWith(m_lastKnownThemeVersion)) &&
1011  (currentLocation == "mainmenu"))
1012  {
1013  m_currentVersion = QString("%1.%2").arg(locMaj).arg(locMin);
1014  m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);
1015 
1016  gCoreContext->SaveSetting("ThemeUpdateStatus",
1017  m_lastKnownThemeVersion + " notified");
1018 
1019  QString message = tr("Version %1 of the %2 theme is now "
1020  "available in the Theme Chooser. The "
1021  "currently installed version is %3.")
1022  .arg(m_newVersion)
1023  .arg(GetMythUI()->GetThemeName())
1024  .arg(m_currentVersion);
1025 
1026  ShowOkPopup(message);
1027  }
1028  }
1029 
1030  delete remoteTheme;
1031  delete localTheme;
1032  }
1033  }
1034 }
1035 
1036 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1037