MythTV  0.27pre
 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_fullPreviewShowing(false),
76  m_fullPreviewStateType(NULL),
77  m_fullScreenName(NULL),
78  m_fullScreenPreview(NULL),
79  m_refreshDownloadableThemes(false),
80  m_downloadTheme(NULL),
81  m_downloadState(dsIdle),
82  m_popupMenu(NULL)
83 {
85 
86  StorageGroup sgroup("Themes", gCoreContext->GetHostName());
87  m_userThemeDir = sgroup.GetFirstDir(true);
88 }
89 
91 {
93 }
94 
95 static bool sortThemeNames(const QFileInfo &s1, const QFileInfo &s2)
96 {
97  return s1.fileName().toLower() < s2.fileName().toLower();
98 }
99 
100 
102 {
103  // Load the theme for this screen
104  if (!LoadWindowFromXML("settings-ui.xml", "themechooser", this))
105  return false;
106 
107  bool err = false;
108  UIUtilE::Assign(this, m_themes, "themes", &err);
109 
110  UIUtilW::Assign(this, m_preview, "preview");
111  UIUtilW::Assign(this, m_fullPreviewStateType, "fullpreviewstate");
112 
114  {
115  MythUIGroup *state =
116  dynamic_cast<MythUIGroup*>
117  (m_fullPreviewStateType->GetChild("fullscreen"));
118  if (state)
119  {
121  dynamic_cast<MythUIText*>(state->GetChild("fullscreenname"));
123  dynamic_cast<MythUIImage*>(state->GetChild("fullscreenpreview"));
124  }
125  }
126 
127  if (err)
128  {
129  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load screen 'themechooser'");
130  return false;
131  }
132 
133  connect(m_themes, SIGNAL(itemClicked(MythUIButtonListItem*)),
134  this, SLOT(saveAndReload(MythUIButtonListItem*)));
135  connect(m_themes, SIGNAL(itemSelected(MythUIButtonListItem*)),
136  this, SLOT(itemChanged(MythUIButtonListItem*)));
137 
138  BuildFocusList();
139 
141 
142  return true;
143 }
144 
146 {
147  SetBusyPopupMessage(tr("Loading Installed Themes"));
148 
149  QString MythVersion = MYTH_SOURCE_PATH;
150  QStringList themesSeen;
151  QDir themes(m_userThemeDir);
152  themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
153  themes.setSorting(QDir::Name | QDir::IgnoreCase);
154 
155  // FIXME: For now, treat git master the same as svn trunk
156  if (MythVersion == "master")
157  MythVersion = "trunk";
158 
159  if (MythVersion != "trunk")
160  {
161  MythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
162  MythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
163  }
164 
165  m_infoList = themes.entryInfoList();
166 
167  for( QFileInfoList::iterator it = m_infoList.begin();
168  it != m_infoList.end();
169  ++it )
170  {
171  if (loadThemeInfo(*it))
172  {
173  themesSeen << (*it).fileName();
174  m_themeStatuses[(*it).fileName()] = "default";
175  }
176  }
177 
178  themes.setPath(GetThemesParentDir());
179  QFileInfoList sharedThemes = themes.entryInfoList();
180  for( QFileInfoList::iterator it = sharedThemes.begin();
181  it != sharedThemes.end();
182  ++it )
183  {
184  if ((!themesSeen.contains((*it).fileName())) &&
185  (loadThemeInfo(*it)))
186  {
187  m_infoList << *it;
188  themesSeen << (*it).fileName();
189  m_themeStatuses[(*it).fileName()] = "default";
190  }
191  }
192 
193  QString remoteThemesFile = GetConfDir();
194  remoteThemesFile.append("/tmp/themes.zip");
195  QString themeSite = QString("%1/%2")
196  .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
197  "http://themes.mythtv.org/themes/repository")).arg(MythVersion);
198  QDir remoteThemesDir(GetMythUI()->GetThemeCacheDir()
199  .append("/themechooser/").append(MythVersion));
200 
201  int downloadFailures =
202  gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
203  if (QFile::exists(remoteThemesFile))
204  {
205  QFileInfo finfo(remoteThemesFile);
206  if (finfo.lastModified().toUTC() <
207  MythDate::current().addSecs(-600))
208  {
209  LOG(VB_GUI, LOG_INFO, LOC +
210  QString("%1 is over 10 minutes old, forcing "
211  "remote theme list download").arg(remoteThemesFile));
213  }
214 
215  if (!remoteThemesDir.exists())
217  }
218  else if (downloadFailures < 2) // (and themes.zip does not exist)
219  {
220  LOG(VB_GUI, LOG_INFO, LOC +
221  QString("%1 does not exist, forcing remote theme "
222  "list download").arg(remoteThemesFile));
224  }
225 
227  {
228  SetBusyPopupMessage(tr("Refreshing Downloadable Themes Information"));
229 
230  QString url = themeSite;
231  url.append("/themes.zip");
232  QString destdir = GetMythUI()->GetThemeCacheDir();
233  destdir.append("/themechooser");
234  QString versiondir = QString("%1/%2").arg(destdir).arg(MythVersion);
235  removeThemeDir(versiondir);
236  QDir dir;
237  dir.mkpath(destdir);
238  bool result = GetMythDownloadManager()->download(url, remoteThemesFile);
239 
240  SetBusyPopupMessage(tr("Extracting Downloadable Themes Information"));
241 
242  if (!result || !extractZIP(remoteThemesFile, destdir))
243  {
244  QFile::remove(remoteThemesFile);
245 
246  downloadFailures++;
247  gCoreContext->SaveSetting("ThemeInfoDownloadFailures",
248  downloadFailures);
249  }
250  }
251 
252  if ((QFile::exists(remoteThemesFile)) &&
253  (remoteThemesDir.exists()))
254  {
255  SetBusyPopupMessage(tr("Loading Downloadable Themes"));
256 
257  LOG(VB_GUI, LOG_INFO, LOC +
258  QString("%1 and %2 exist, using cached remote themes list")
259  .arg(remoteThemesFile).arg(remoteThemesDir.absolutePath()));
260 
261  QString themesPath = remoteThemesDir.absolutePath();
262  themes.setPath(themesPath);
263 
264  QFileInfoList downloadableThemes = themes.entryInfoList();
265  for( QFileInfoList::iterator it = downloadableThemes.begin();
266  it != downloadableThemes.end();
267  ++it )
268  {
269  QString dirName = (*it).fileName();
270  QString themeName = dirName;
271  QString remoteDir = themeSite;
272  remoteDir.append("/").append(dirName);
273  QString localDir = themes.absolutePath();
274  localDir.append("/").append(dirName);
275 
276  if (themesSeen.contains(dirName))
277  {
278  ThemeInfo remoteTheme((*it).absoluteFilePath());
279  ThemeInfo *localTheme = m_themeNameInfos[dirName];
280 
281  themeName = remoteTheme.GetName();
282 
283  int rmtMaj = remoteTheme.GetMajorVersion();
284  int rmtMin = remoteTheme.GetMinorVersion();
285  int locMaj = localTheme->GetMajorVersion();
286  int locMin = localTheme->GetMinorVersion();
287 
288  if ((rmtMaj > locMaj) ||
289  ((rmtMaj == locMaj) &&
290  (rmtMin > locMin)))
291  {
292  if (loadThemeInfo(*it))
293  {
294  m_infoList << *it;
295  m_themeStatuses[themeName] = "updateavailable";
296 
297  QFileInfo finfo(remoteTheme.GetPreviewPath());
299  remoteDir.append("/").append(finfo.fileName()),
300  localDir.append("/").append(finfo.fileName()),
301  NULL);
302  }
303  }
304  else if ((rmtMaj == locMaj) &&
305  (rmtMin == locMin))
306  {
307  m_themeStatuses[themeName] = "uptodate";
308  }
309  }
310  else
311  {
312  ThemeInfo *remoteTheme = loadThemeInfo(*it);
313  if (remoteTheme)
314  {
315  themeName = remoteTheme->GetName();
316  themesSeen << dirName;
317  m_infoList << *it;
318  m_themeStatuses[themeName] = "updateavailable";
319 
320  QFileInfo finfo(remoteTheme->GetPreviewPath());
322  remoteDir.append("/").append(finfo.fileName()),
323  localDir.append("/").append(finfo.fileName()),
324  NULL);
325  }
326  }
327  }
328  }
329 
330  ResetBusyPopup();
331 
332  qSort(m_infoList.begin(), m_infoList.end(), sortThemeNames);
333 }
334 
336 {
337  QString curTheme = gCoreContext->GetSetting("Theme");
338  ThemeInfo *themeinfo = NULL;
339  ThemeInfo *curThemeInfo = NULL;
340  MythUIButtonListItem *item = NULL;
341 
342  m_themes->Reset();
343  for( QFileInfoList::iterator it = m_infoList.begin();
344  it != m_infoList.end();
345  ++it )
346  {
347  QFileInfo &theme = *it;
348 
349  if (!m_themeFileNameInfos.contains(theme.filePath()))
350  continue;
351 
352  themeinfo = m_themeFileNameInfos[theme.filePath()];
353  if (!themeinfo)
354  continue;
355 
356  QString buttonText = QString("%1 %2.%3")
357  .arg(themeinfo->GetName())
358  .arg(themeinfo->GetMajorVersion())
359  .arg(themeinfo->GetMinorVersion());
360 
361  item = new MythUIButtonListItem(m_themes, buttonText);
362  if (item)
363  {
364  if (themeinfo->GetDownloadURL().isEmpty())
365  item->DisplayState("local", "themelocation");
366  else
367  item->DisplayState("remote", "themelocation");
368 
369  item->DisplayState(themeinfo->GetAspect(), "aspectstate");
370 
371  item->DisplayState(m_themeStatuses[themeinfo->GetName()],
372  "themestatus");
373  QHash<QString, QString> infomap;
374  themeinfo->ToMap(infomap);
375  item->SetTextFromMap(infomap);
376  item->SetData(qVariantFromValue(themeinfo));
377 
378  QString thumbnail = themeinfo->GetPreviewPath();
379  QFileInfo fInfo(thumbnail);
380  // Downloadable themeinfos have thumbnail copies of their preview images
381  if (!themeinfo->GetDownloadURL().isEmpty())
382  thumbnail = thumbnail.append(".thumb.jpg");
383  item->SetImage(thumbnail);
384 
385  if (curTheme == themeinfo->GetDirectoryName())
386  curThemeInfo = themeinfo;
387  }
388  else
389  delete item;
390  }
391 
393 
394  if (curThemeInfo)
395  m_themes->SetValueByData(qVariantFromValue(curThemeInfo));
396 
398  if (current)
399  itemChanged(current);
400 }
401 
403 {
404  if (theme.fileName() == "default" || theme.fileName() == "default-wide")
405  return NULL;
406 
407  ThemeInfo *themeinfo = NULL;
408  if (theme.exists()) // local directory vs http:// or remote URL
409  themeinfo = new ThemeInfo(theme.absoluteFilePath());
410  else
411  themeinfo = new ThemeInfo(theme.filePath());
412 
413  if (!themeinfo)
414  return NULL;
415 
416  if (themeinfo->GetName().isEmpty() || !(themeinfo->GetType() & THEME_UI))
417  {
418  delete themeinfo;
419  return NULL;
420  }
421 
422  m_themeFileNameInfos[theme.filePath()] = themeinfo;
423  m_themeNameInfos[theme.fileName()] = themeinfo;
424 
425  return themeinfo;
426 }
427 
429 {
430  if (m_popupMenu)
431  return;
432 
433  MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
434  QString label = tr("Theme Chooser Menu");
435 
436  m_popupMenu =
437  new MythDialogBox(label, popupStack, "themechoosermenupopup");
438 
439  connect(m_popupMenu, SIGNAL(Closed(QString, int)), SLOT(popupClosed(QString, int)));
440 
441  if (m_popupMenu->Create())
442  popupStack->AddScreen(m_popupMenu);
443  else
444  {
445  delete m_popupMenu;
446  m_popupMenu = NULL;
447  return;
448  }
449 
450  m_popupMenu->SetReturnEvent(this, "popupmenu");
451 
453  {
455  m_popupMenu->AddButton(tr("Hide Fullscreen Preview"),
456  SLOT(toggleFullscreenPreview()));
457  else
458  m_popupMenu->AddButton(tr("Show Fullscreen Preview"),
459  SLOT(toggleFullscreenPreview()));
460  }
461 
462  m_popupMenu->AddButton(tr("Refresh Downloadable Themes"),
463  SLOT(refreshDownloadableThemes()));
464 
466  if (current)
467  {
468  ThemeInfo *info = qVariantValue<ThemeInfo *>(current->GetData());
469 
470  if (info)
471  {
472  m_popupMenu->AddButton(tr("Select Theme"),
473  SLOT(saveAndReload()));
474 
475  if (info->GetPreviewPath().startsWith(m_userThemeDir))
476  m_popupMenu->AddButton(tr("Delete Theme"),
477  SLOT(removeTheme()));
478  }
479  }
480 
481  if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
482  m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
484  else
485  m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
487 }
488 
489 void ThemeChooser::popupClosed(QString which, int result)
490 {
491  (void)which;
492  (void)result;
493 
494  m_popupMenu = NULL;
495 }
496 
497 bool ThemeChooser::keyPressEvent(QKeyEvent *event)
498 {
499  if (GetFocusWidget()->keyPressEvent(event))
500  return true;
501 
502  bool handled = false;
503  QStringList actions;
504  handled = GetMythMainWindow()->TranslateKeyPress("Theme Chooser", event, actions);
505 
506  for (int i = 0; i < actions.size() && !handled; ++i)
507  {
508  QString action = actions[i];
509  handled = true;
510 
511  if (action == "MENU")
512  showPopupMenu();
513  else if (action == "DELETE")
514  removeTheme();
515  else if ((action == "ESCAPE") &&
517  {
519  }
520  else
521  handled = false;
522  }
523 
524  if (!handled && MythScreenType::keyPressEvent(event))
525  handled = true;
526 
527  return handled;
528 }
529 
531 {
533  {
535  {
538 
539  if (m_fullScreenName)
541 
543  m_fullPreviewShowing = false;
544  }
545  else
546  {
548  ThemeInfo *info = qVariantValue<ThemeInfo*>(item->GetData());
549  if (info)
550  {
552  {
555  }
556 
557  if (m_fullScreenName)
558  m_fullScreenName->SetText(info->GetName());
559 
560  m_fullPreviewStateType->DisplayState("fullscreen");
561  m_fullPreviewShowing = true;
562  }
563  }
564  }
565 }
566 
568 {
569  if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
570  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
571  else
572  gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
573 }
574 
576 {
577  LOG(VB_GUI, LOG_INFO, LOC + "Forcing remote theme list refresh");
579  gCoreContext->SaveSetting("ThemeInfoDownloadFailures", 0);
581 }
582 
584 {
586  if (current)
587  saveAndReload(current);
588 }
589 
591 {
592  ThemeInfo *info = qVariantValue<ThemeInfo *>(item->GetData());
593 
594  if (!info)
595  return;
596 
597  if (!info->GetDownloadURL().isEmpty())
598  {
599  QString downloadURL = info->GetDownloadURL();
600  QFileInfo qfile(downloadURL);
601  QString baseName = qfile.fileName();
602 
603  if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
604  {
605  QStringList tokens =
606  gCoreContext->GetSetting("ThemeDownloadURL")
607  .split(";", QString::SkipEmptyParts);
608  QString origURL = downloadURL;
609  downloadURL.replace(tokens[0], tokens[1]);
610  LOG(VB_FILE, LOG_WARNING, LOC +
611  QString("Theme download URL overridden from %1 to %2.")
612  .arg(origURL).arg(downloadURL));
613  }
614 
615  OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
616  m_downloadTheme = info;
617 #if 0
618  m_downloadFile = RemoteDownloadFile(downloadURL,
619  "Temp", baseName);
621 #else
622  QString localFile = GetConfDir() + "/tmp/" + baseName;
623  GetMythDownloadManager()->queueDownload(downloadURL, localFile, this);
624  m_downloadFile = localFile;
626 #endif
627  }
628  else
629  {
630  gCoreContext->SaveSetting("Theme", info->GetDirectoryName());
631  GetMythMainWindow()->JumpTo("Reload Theme");
632  }
633 }
634 
636 {
637  ThemeInfo *info = qVariantValue<ThemeInfo*>(item->GetData());
638 
639  if (!info)
640  return;
641 
642  QFileInfo preview(info->GetPreviewPath());
643  QHash<QString, QString> infomap;
644  info->ToMap(infomap);
645  SetTextFromMap(infomap);
646  if (m_preview)
647  {
648  if (preview.exists())
649  {
651  m_preview->Load();
652  }
653  else
654  m_preview->Reset();
655  }
657  {
659  {
660  if (preview.exists())
661  {
664  }
665  else
667  }
668 
669  if (m_fullScreenName)
670  m_fullScreenName->SetText(info->GetName());
671  }
672 
673  MythUIStateType *themeLocation =
674  dynamic_cast<MythUIStateType*>(GetChild("themelocation"));
675  if (themeLocation)
676  {
677  if (info->GetDownloadURL().isEmpty())
678  themeLocation->DisplayState("local");
679  else
680  themeLocation->DisplayState("remote");
681  }
682 
683  MythUIStateType *aspectState =
684  dynamic_cast<MythUIStateType*>(GetChild("aspectstate"));
685  if (aspectState)
686  aspectState->DisplayState(info->GetAspect());
687 }
688 
689 void ThemeChooser::updateProgressBar(int bytesReceived,
690  int bytesTotal)
691 {
692  MythUIProgressBar *progressBar =
693  dynamic_cast<MythUIProgressBar *>(GetChild("downloadprogressbar"));
694 
695  if (!progressBar)
696  return;
697 
698  progressBar->SetUsed(bytesReceived);
699  progressBar->SetTotal(bytesTotal);
700 }
701 
703 {
704  if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
705  {
706  MythEvent *me = (MythEvent *)e;
707  QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
708 
709  if (tokens.isEmpty())
710  return;
711 
712  if (tokens[0] == "DOWNLOAD_FILE")
713  {
714  QStringList args = me->ExtraDataList();
715  if ((m_downloadState == dsIdle) ||
716  (tokens.size() != 2) ||
717  (!m_downloadTheme) ||
718  (args[1] != m_downloadFile))
719  return;
720 
721  if (tokens[1] == "UPDATE")
722  {
723  updateProgressBar(args[2].toInt(), args[3].toInt());
724  }
725  else if (tokens[1] == "FINISHED")
726  {
727  bool remoteFileIsLocal = false;
728  int fileSize = args[2].toInt();
729  int errorCode = args[4].toInt();
730 
731  CloseBusyPopup();
732 
733  QFileInfo file(m_downloadFile);
735  (m_downloadFile.startsWith("myth://")))
736  {
737  // The backend download is finished so start the
738  // frontend download
739  if ((errorCode == 0) &&
740  (fileSize > 0))
741  {
743  QString localFile = GetConfDir() + "/tmp/" +
744  file.fileName();
745  file.setFile(localFile);
746 
747  if (file.exists())
748  {
749  remoteFileIsLocal = true;
750  m_downloadFile = localFile;
751  }
752  else
753  {
755  m_downloadFile, localFile, this);
756  OpenBusyPopup(tr("Copying %1 Theme Package")
757  .arg(m_downloadTheme->GetName()));
758  m_downloadFile = localFile;
759  return;
760  }
761  }
762  else
763  {
765  ShowOkPopup(tr("ERROR downloading theme package on master backend."));
766  }
767  }
768 
770  (file.exists()))
771  {
772  // The frontend download is finished
773  if ((errorCode == 0) &&
774  (fileSize > 0))
775  {
777  ThemeExtractThread *extractThread =
781  extractThread, "ThemeExtract");
782 
783  if (!remoteFileIsLocal)
784  RemoteFile::DeleteFile(args[0]);
785 
786  OpenBusyPopup(tr("Installing %1 Theme")
787  .arg(m_downloadTheme->GetName()));
788  }
789  else
790  {
792  ShowOkPopup(tr("ERROR downloading theme package from master backend."));
793  }
794  }
795  }
796  }
797  else if ((me->Message() == "THEME_INSTALLED") &&
798  (m_downloadTheme) &&
800  {
802  CloseBusyPopup();
803  QStringList args = me->ExtraDataList();
804  QFile::remove(args[0]);
805 
806  QString event = QString("THEME_INSTALLED PATH %1")
807  .arg(m_userThemeDir +
810 
812  GetMythMainWindow()->JumpTo("Reload Theme");
813  }
814  }
815 }
816 
818 {
820  if (!current)
821  {
822  ShowOkPopup(tr("Error, no theme selected."));
823  return;
824  }
825 
826  ThemeInfo *info = qVariantValue<ThemeInfo *>(current->GetData());
827  if (!info)
828  {
829  ShowOkPopup(tr("Error, unable to find current theme."));
830  return;
831  }
832 
833  if (!info->GetPreviewPath().startsWith(m_userThemeDir))
834  {
835  ShowOkPopup(tr("%1 is not a user-installed theme and can not "
836  "be deleted.").arg(info->GetName()));
837  return;
838  }
839 
841 
843 }
844 
846 {
847  if ((!dirname.startsWith(m_userThemeDir)) &&
848  (!dirname.startsWith(GetMythUI()->GetThemeCacheDir())))
849  return;
850 
851  QDir dir(dirname);
852 
853  if (!dir.exists())
854  return;
855 
856  dir.setFilter(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
857  QFileInfoList list = dir.entryInfoList();
858  QFileInfoList::const_iterator it = list.begin();
859  const QFileInfo *fi;
860 
861  while (it != list.end())
862  {
863  fi = &(*it++);
864  if (fi->isFile() && !fi->isSymLink())
865  {
866  QFile::remove(fi->absoluteFilePath());
867  }
868  else if (fi->isDir() && !fi->isSymLink())
869  {
870  removeThemeDir(fi->absoluteFilePath());
871  }
872  }
873 
874  dir.rmdir(dirname);
875 }
876 
878 
880  m_updateTimer(new QTimer(this))
881 {
882  m_mythVersion = MYTH_SOURCE_PATH;
883 
884  // FIXME: For now, treat git master the same as svn trunk
885  if (m_mythVersion == "master")
886  m_mythVersion = "trunk";
887 
888  if (m_mythVersion != "trunk")
889  {
890  m_mythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
891  m_mythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
892  }
893 
895  0,
896  "remotethemes/themes.zip",
897  "Temp");
898 
899  gCoreContext->SaveSetting("ThemeUpdateStatus", "");
900 
901  connect(m_updateTimer, SIGNAL(timeout()), SLOT(checkForUpdate()));
902  m_updateTimer->start(60 * 60 * 1000); // Run once an hour
903 
904  // Run once 15 seconds from now
905  QTimer::singleShot(15 * 1000, this, SLOT(checkForUpdate()));
906 }
907 
909 {
910  if (m_updateTimer)
911  {
912  m_updateTimer->stop();
913  delete m_updateTimer;
914  m_updateTimer = NULL;
915  }
916 }
917 
919 {
920  if (GetMythUI()->GetCurrentLocation(false, true) != "mainmenu")
921  return;
922 
924  {
925  QString remoteThemeDir =
926  gCoreContext->GenMythURL(gCoreContext->GetSetting("MasterServerIP"),
927  0,
928  QString("remotethemes/%1/%2")
929  .arg(m_mythVersion)
930  .arg(GetMythUI()->GetThemeName()),
931  "Temp");
932 
933  QString infoXML = remoteThemeDir;
934  infoXML.append("/themeinfo.xml");
935 
936  if (RemoteFile::Exists(infoXML))
937  {
938  ThemeInfo *remoteTheme = new ThemeInfo(remoteThemeDir);
939  if (!remoteTheme)
940  {
941  LOG(VB_GENERAL, LOG_ERR,
942  QString("ThemeUpdateChecker::checkForUpdate(): "
943  "Unable to create ThemeInfo for %1")
944  .arg(infoXML));
945  return;
946  }
947 
948  ThemeInfo *localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
949  if (!localTheme)
950  {
951  LOG(VB_GENERAL, LOG_ERR,
952  "ThemeUpdateChecker::checkForUpdate(): "
953  "Unable to create ThemeInfo for current theme");
954  return;
955  }
956 
957  int rmtMaj = remoteTheme->GetMajorVersion();
958  int rmtMin = remoteTheme->GetMinorVersion();
959  int locMaj = localTheme->GetMajorVersion();
960  int locMin = localTheme->GetMinorVersion();
961 
962  if ((rmtMaj > locMaj) ||
963  ((rmtMaj == locMaj) &&
964  (rmtMin > locMin)))
965  {
967  QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
968  .arg(rmtMaj).arg(rmtMin);
969 
970  QString status = gCoreContext->GetSetting("ThemeUpdateStatus");
971  QString currentLocation = GetMythUI()->GetCurrentLocation(false, true);
972 
973  if ((!status.startsWith(m_lastKnownThemeVersion)) &&
974  (currentLocation == "mainmenu"))
975  {
976  m_currentVersion = QString("%1.%2").arg(locMaj).arg(locMin);
977  m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);
978 
979  gCoreContext->SaveSetting("ThemeUpdateStatus",
980  m_lastKnownThemeVersion + " notified");
981 
982  QString message = tr("Version %1 of the %2 theme is now "
983  "available in the Theme Chooser. The "
984  "currently installed version is %3.")
985  .arg(m_newVersion)
986  .arg(GetMythUI()->GetThemeName())
987  .arg(m_currentVersion);
988 
989  ShowOkPopup(message);
990  }
991  }
992 
993  delete remoteTheme;
994  delete localTheme;
995  }
996  }
997 }
998 
999 /* vim: set expandtab tabstop=4 shiftwidth=4: */
1000