MythTV  0.26-pre
themechooser.cpp
Go to the documentation of this file.
00001 
00002 // Theme Chooser headers
00003 #include "themechooser.h"
00004 
00005 // Qt headers
00006 #include <QCoreApplication>
00007 #include <QRunnable>
00008 #include <QRegExp>
00009 
00010 // MythTV headers
00011 #include "mythcorecontext.h"
00012 #include "mythcoreutil.h"
00013 #include "mthreadpool.h"
00014 #include "remotefile.h"
00015 #include "mythdownloadmanager.h"
00016 #include "programtypes.h"
00017 #include "mythsystemevent.h"
00018 #include "mythmiscutil.h"
00019 #include "mythversion.h"
00020 #include "mythlogging.h"
00021 #include "storagegroup.h"
00022 
00023 // LibMythUI headers
00024 #include "mythmainwindow.h"
00025 #include "mythuihelper.h"
00026 #include "mythuiprogressbar.h"
00027 #include "mythdialogbox.h"
00028 #include "mythuibuttonlist.h"
00029 #include "mythscreenstack.h"
00030 #include "mythuistatetype.h"
00031 
00032 #define LOC      QString("ThemeChooser: ")
00033 #define LOC_WARN QString("ThemeChooser, Warning: ")
00034 #define LOC_ERR  QString("ThemeChooser, Error: ")
00035 
00039 class ThemeExtractThread : public QRunnable
00040 {
00041   public:
00042     ThemeExtractThread(ThemeChooser *parent,
00043                        const QString &srcFile, const QString &destDir) :
00044         m_parent(parent),
00045         m_srcFile(srcFile),
00046         m_destDir(destDir)
00047     {
00048         m_srcFile.detach();
00049         m_destDir.detach();
00050     }
00051 
00052     void run()
00053     {
00054         extractZIP(m_srcFile, m_destDir);
00055 
00056         MythEvent *me =
00057              new MythEvent("THEME_INSTALLED", QStringList(m_srcFile));
00058         QCoreApplication::postEvent(m_parent, me);
00059     }
00060 
00061   private:
00062     ThemeChooser        *m_parent;
00063     QString              m_srcFile;
00064     QString              m_destDir;
00065 };
00066 
00067 
00072 ThemeChooser::ThemeChooser(MythScreenStack *parent,
00073                            const QString name) :
00074     MythScreenType(parent, name),
00075     m_fullPreviewShowing(false),
00076     m_fullPreviewStateType(NULL),
00077     m_fullScreenName(NULL),
00078     m_fullScreenPreview(NULL),
00079     m_refreshDownloadableThemes(false),
00080     m_downloadTheme(NULL),
00081     m_downloadState(dsIdle),
00082     m_popupMenu(NULL)
00083 {
00084     gCoreContext->addListener(this);
00085 
00086     StorageGroup sgroup("Themes", gCoreContext->GetHostName());
00087     m_userThemeDir = sgroup.GetFirstDir(true);
00088 }
00089 
00090 ThemeChooser::~ThemeChooser()
00091 {
00092     gCoreContext->removeListener(this);
00093 }
00094 
00095 static bool sortThemeNames(const QFileInfo &s1, const QFileInfo &s2)
00096 {
00097     return s1.fileName().toLower() < s2.fileName().toLower();
00098 }
00099 
00100 
00101 bool ThemeChooser::Create(void)
00102 {
00103     // Load the theme for this screen
00104     if (!LoadWindowFromXML("settings-ui.xml", "themechooser", this))
00105         return false;
00106 
00107     bool err = false;
00108     UIUtilE::Assign(this, m_themes, "themes", &err);
00109 
00110     UIUtilW::Assign(this, m_preview, "preview");
00111     UIUtilW::Assign(this, m_fullPreviewStateType, "fullpreviewstate");
00112 
00113     if (m_fullPreviewStateType)
00114     {
00115         MythUIGroup *state =
00116             dynamic_cast<MythUIGroup*>
00117                 (m_fullPreviewStateType->GetChild("fullscreen"));
00118         if (state)
00119         {
00120             m_fullScreenName =
00121                dynamic_cast<MythUIText*>(state->GetChild("fullscreenname"));
00122             m_fullScreenPreview =
00123                dynamic_cast<MythUIImage*>(state->GetChild("fullscreenpreview"));
00124         }
00125     }
00126 
00127     if (err)
00128     {
00129         LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot load screen 'themechooser'");
00130         return false;
00131     }
00132 
00133     connect(m_themes, SIGNAL(itemClicked(MythUIButtonListItem*)),
00134             this, SLOT(saveAndReload(MythUIButtonListItem*)));
00135     connect(m_themes, SIGNAL(itemSelected(MythUIButtonListItem*)),
00136             this, SLOT(itemChanged(MythUIButtonListItem*)));
00137 
00138     BuildFocusList();
00139 
00140     LoadInBackground();
00141 
00142     return true;
00143 }
00144 
00145 void ThemeChooser::Load(void)
00146 {
00147     SetBusyPopupMessage(tr("Loading Installed Themes"));
00148 
00149     QString MythVersion = MYTH_SOURCE_PATH;
00150     QStringList themesSeen;
00151     QDir themes(m_userThemeDir);
00152     themes.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
00153     themes.setSorting(QDir::Name | QDir::IgnoreCase);
00154 
00155     // FIXME: For now, treat git master the same as svn trunk
00156     if (MythVersion == "master")
00157         MythVersion = "trunk";
00158 
00159     if (MythVersion != "trunk")
00160     {
00161         MythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
00162         MythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
00163     }
00164 
00165     m_infoList = themes.entryInfoList();
00166 
00167     for( QFileInfoList::iterator it =  m_infoList.begin();
00168                                  it != m_infoList.end();
00169                                ++it )
00170     {
00171         if (loadThemeInfo(*it))
00172         {
00173             themesSeen << (*it).fileName();
00174             m_themeStatuses[(*it).fileName()] = "default";
00175         }
00176     }
00177 
00178     themes.setPath(GetThemesParentDir());
00179     QFileInfoList sharedThemes = themes.entryInfoList();
00180     for( QFileInfoList::iterator it =  sharedThemes.begin();
00181                                  it != sharedThemes.end();
00182                                ++it )
00183     {
00184         if ((!themesSeen.contains((*it).fileName())) &&
00185             (loadThemeInfo(*it)))
00186         {
00187             m_infoList << *it;
00188             themesSeen << (*it).fileName();
00189             m_themeStatuses[(*it).fileName()] = "default";
00190         }
00191     }
00192 
00193     QString remoteThemesFile = GetConfDir();
00194     remoteThemesFile.append("/tmp/themes.zip");
00195     QString themeSite = QString("%1/%2")
00196         .arg(gCoreContext->GetSetting("ThemeRepositoryURL",
00197              "http://themes.mythtv.org/themes/repository")).arg(MythVersion);
00198     QDir remoteThemesDir(GetMythUI()->GetThemeCacheDir()
00199                              .append("/themechooser/").append(MythVersion));
00200 
00201     int downloadFailures =
00202         gCoreContext->GetNumSetting("ThemeInfoDownloadFailures", 0);
00203     if (QFile::exists(remoteThemesFile))
00204     {
00205         QFileInfo finfo(remoteThemesFile);
00206         if (finfo.lastModified() < mythCurrentDateTime().addSecs(-600))
00207         {
00208             LOG(VB_GUI, LOG_INFO, LOC +
00209                 QString("%1 is over 10 minutes old, forcing "
00210                         "remote theme list download").arg(remoteThemesFile));
00211             m_refreshDownloadableThemes = true;
00212         }
00213 
00214         if (!remoteThemesDir.exists())
00215             m_refreshDownloadableThemes = true;
00216     }
00217     else if (downloadFailures < 2) // (and themes.zip does not exist)
00218     {
00219         LOG(VB_GUI, LOG_INFO, LOC +
00220             QString("%1 does not exist, forcing remote theme "
00221                     "list download").arg(remoteThemesFile));
00222         m_refreshDownloadableThemes = true;
00223     }
00224 
00225     if (m_refreshDownloadableThemes)
00226     {
00227         SetBusyPopupMessage(tr("Refreshing Downloadable Themes Information"));
00228 
00229         QString url = themeSite;
00230         url.append("/themes.zip");
00231         QString destdir = GetMythUI()->GetThemeCacheDir();
00232         destdir.append("/themechooser");
00233         QString versiondir = QString("%1/%2").arg(destdir).arg(MythVersion);
00234         removeThemeDir(versiondir);
00235         QDir dir;
00236         dir.mkpath(destdir);
00237         bool result = GetMythDownloadManager()->download(url, remoteThemesFile);
00238 
00239         SetBusyPopupMessage(tr("Extracting Downloadable Themes Information"));
00240 
00241         if (!result || !extractZIP(remoteThemesFile, destdir))
00242         {
00243             QFile::remove(remoteThemesFile);
00244 
00245             downloadFailures++;
00246             gCoreContext->SaveSetting("ThemeInfoDownloadFailures",
00247                                       downloadFailures);
00248         }
00249     }
00250 
00251     if ((QFile::exists(remoteThemesFile)) &&
00252         (remoteThemesDir.exists()))
00253     {
00254         SetBusyPopupMessage(tr("Loading Downloadable Themes"));
00255 
00256         LOG(VB_GUI, LOG_INFO, LOC +
00257             QString("%1 and %2 exist, using cached remote themes list")
00258                 .arg(remoteThemesFile).arg(remoteThemesDir.absolutePath()));
00259 
00260         QString themesPath = remoteThemesDir.absolutePath();
00261         themes.setPath(themesPath);
00262 
00263         QFileInfoList downloadableThemes = themes.entryInfoList();
00264         for( QFileInfoList::iterator it =  downloadableThemes.begin();
00265                                      it != downloadableThemes.end();
00266                                    ++it )
00267         {
00268             QString dirName = (*it).fileName();
00269             QString themeName = dirName;
00270             QString remoteDir = themeSite;
00271             remoteDir.append("/").append(dirName);
00272             QString localDir = themes.absolutePath();
00273             localDir.append("/").append(dirName);
00274 
00275             if (themesSeen.contains(dirName))
00276             {
00277                 ThemeInfo remoteTheme((*it).absoluteFilePath());
00278                 ThemeInfo *localTheme = m_themeNameInfos[dirName];
00279 
00280                 themeName = remoteTheme.GetName();
00281 
00282                 int rmtMaj = remoteTheme.GetMajorVersion();
00283                 int rmtMin = remoteTheme.GetMinorVersion();
00284                 int locMaj = localTheme->GetMajorVersion();
00285                 int locMin = localTheme->GetMinorVersion();
00286 
00287                 if ((rmtMaj > locMaj) ||
00288                     ((rmtMaj == locMaj) &&
00289                      (rmtMin > locMin)))
00290                 {
00291                     if (loadThemeInfo(*it))
00292                     {
00293                         m_infoList << *it;
00294                         m_themeStatuses[themeName] = "updateavailable";
00295 
00296                         QFileInfo finfo(remoteTheme.GetPreviewPath());
00297                         GetMythDownloadManager()->queueDownload(
00298                             remoteDir.append("/").append(finfo.fileName()),
00299                             localDir.append("/").append(finfo.fileName()),
00300                             NULL);
00301                     }
00302                 }
00303                 else if ((rmtMaj == locMaj) &&
00304                          (rmtMin == locMin))
00305                 {
00306                     m_themeStatuses[themeName] = "uptodate";
00307                 }
00308             }
00309             else
00310             {
00311                 ThemeInfo *remoteTheme = loadThemeInfo(*it);
00312                 if (remoteTheme)
00313                 {
00314                     themeName = remoteTheme->GetName();
00315                     themesSeen << dirName;
00316                     m_infoList << *it;
00317                     m_themeStatuses[themeName] = "updateavailable";
00318 
00319                     QFileInfo finfo(remoteTheme->GetPreviewPath());
00320                     GetMythDownloadManager()->queueDownload(
00321                         remoteDir.append("/").append(finfo.fileName()),
00322                         localDir.append("/").append(finfo.fileName()),
00323                         NULL);
00324                 }
00325             }
00326         }
00327     }
00328 
00329     ResetBusyPopup();
00330 
00331     qSort(m_infoList.begin(), m_infoList.end(), sortThemeNames);
00332 }
00333 
00334 void ThemeChooser::Init(void)
00335 {
00336     QString curTheme = gCoreContext->GetSetting("Theme");
00337     ThemeInfo *themeinfo = NULL;
00338     ThemeInfo *curThemeInfo = NULL;
00339     MythUIButtonListItem *item = NULL;
00340 
00341     m_themes->Reset();
00342     for( QFileInfoList::iterator it =  m_infoList.begin();
00343                                  it != m_infoList.end();
00344                                ++it )
00345     {
00346         QFileInfo  &theme = *it;
00347 
00348         if (!m_themeFileNameInfos.contains(theme.filePath()))
00349             continue;
00350 
00351         themeinfo = m_themeFileNameInfos[theme.filePath()];
00352         if (!themeinfo)
00353             continue;
00354 
00355         QString buttonText = QString("%1 %2.%3")
00356                                 .arg(themeinfo->GetName())
00357                                 .arg(themeinfo->GetMajorVersion())
00358                                 .arg(themeinfo->GetMinorVersion());
00359 
00360         item = new MythUIButtonListItem(m_themes, buttonText);
00361         if (item)
00362         {
00363             if (themeinfo->GetDownloadURL().isEmpty())
00364                 item->DisplayState("local", "themelocation");
00365             else
00366                 item->DisplayState("remote", "themelocation");
00367 
00368             item->DisplayState(themeinfo->GetAspect(), "aspectstate");
00369 
00370             item->DisplayState(m_themeStatuses[themeinfo->GetName()],
00371                                "themestatus");
00372             QHash<QString, QString> infomap;
00373             themeinfo->ToMap(infomap);
00374             item->SetTextFromMap(infomap);
00375             item->SetData(qVariantFromValue(themeinfo));
00376 
00377             QString thumbnail = themeinfo->GetPreviewPath();
00378             QFileInfo fInfo(thumbnail);
00379             // Downloadable themeinfos have thumbnail copies of their preview images
00380             if (!themeinfo->GetDownloadURL().isEmpty())
00381                 thumbnail = thumbnail.append(".thumb.jpg");
00382             item->SetImage(thumbnail);
00383 
00384             if (curTheme == themeinfo->GetDirectoryName())
00385                 curThemeInfo = themeinfo;
00386         }
00387         else
00388             delete item;
00389     }
00390 
00391     SetFocusWidget(m_themes);
00392 
00393     if (curThemeInfo)
00394         m_themes->SetValueByData(qVariantFromValue(curThemeInfo));
00395 
00396     MythUIButtonListItem *current = m_themes->GetItemCurrent();
00397     if (current)
00398         itemChanged(current);
00399 }
00400 
00401 ThemeInfo *ThemeChooser::loadThemeInfo(QFileInfo &theme)
00402 {
00403     if (theme.fileName() == "default" || theme.fileName() == "default-wide")
00404         return NULL;
00405 
00406     ThemeInfo *themeinfo = NULL;
00407     if (theme.exists()) // local directory vs http:// or remote URL
00408         themeinfo = new ThemeInfo(theme.absoluteFilePath());
00409     else
00410         themeinfo = new ThemeInfo(theme.filePath());
00411 
00412     if (!themeinfo)
00413         return NULL;
00414 
00415     if (themeinfo->GetName().isEmpty() || !(themeinfo->GetType() & THEME_UI))
00416     {
00417         delete themeinfo;
00418         return NULL;
00419     }
00420 
00421     m_themeFileNameInfos[theme.filePath()] = themeinfo;
00422     m_themeNameInfos[theme.fileName()] = themeinfo;
00423 
00424     return themeinfo;
00425 }
00426 
00427 void ThemeChooser::showPopupMenu(void)
00428 {
00429     if (m_popupMenu)
00430         return;
00431 
00432     MythScreenStack *popupStack = GetMythMainWindow()->GetStack("popup stack");
00433     QString label = tr("Theme Chooser Menu");
00434 
00435     m_popupMenu =
00436         new MythDialogBox(label, popupStack, "themechoosermenupopup");
00437 
00438     connect(m_popupMenu, SIGNAL(Closed(QString, int)), SLOT(popupClosed(QString, int)));
00439 
00440     if (m_popupMenu->Create())
00441         popupStack->AddScreen(m_popupMenu);
00442     else
00443     {
00444         delete m_popupMenu;
00445         m_popupMenu = NULL;
00446         return;
00447     }
00448 
00449     m_popupMenu->SetReturnEvent(this, "popupmenu");
00450 
00451     if (m_fullPreviewStateType)
00452     {
00453         if (m_fullPreviewShowing)
00454             m_popupMenu->AddButton(tr("Hide Fullscreen Preview"),
00455                                    SLOT(toggleFullscreenPreview()));
00456         else
00457             m_popupMenu->AddButton(tr("Show Fullscreen Preview"),
00458                                    SLOT(toggleFullscreenPreview()));
00459     }
00460 
00461     m_popupMenu->AddButton(tr("Refresh Downloadable Themes"),
00462                            SLOT(refreshDownloadableThemes()));
00463 
00464     MythUIButtonListItem *current = m_themes->GetItemCurrent();
00465     if (current)
00466     {
00467         ThemeInfo *info = qVariantValue<ThemeInfo *>(current->GetData());
00468 
00469         if (info)
00470         {
00471             m_popupMenu->AddButton(tr("Select Theme"),
00472                                    SLOT(saveAndReload()));
00473 
00474             if (info->GetPreviewPath().startsWith(m_userThemeDir))
00475                 m_popupMenu->AddButton(tr("Delete Theme"),
00476                                        SLOT(removeTheme()));
00477         }
00478     }
00479 
00480     if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
00481         m_popupMenu->AddButton(tr("Disable Theme Update Notifications"),
00482                                SLOT(toggleThemeUpdateNotifications()));
00483     else
00484         m_popupMenu->AddButton(tr("Enable Theme Update Notifications"),
00485                                SLOT(toggleThemeUpdateNotifications()));
00486 }
00487 
00488 void ThemeChooser::popupClosed(QString which, int result)
00489 {
00490     (void)which;
00491     (void)result;
00492 
00493     m_popupMenu = NULL;
00494 }
00495 
00496 bool ThemeChooser::keyPressEvent(QKeyEvent *event)
00497 {
00498     if (GetFocusWidget()->keyPressEvent(event))
00499         return true;
00500 
00501     bool handled = false;
00502     QStringList actions;
00503     handled = GetMythMainWindow()->TranslateKeyPress("Theme Chooser", event, actions);
00504 
00505     for (int i = 0; i < actions.size() && !handled; ++i)
00506     {
00507         QString action = actions[i];
00508         handled = true;
00509 
00510         if (action == "MENU")
00511             showPopupMenu();
00512         else if (action == "DELETE")
00513             removeTheme();
00514         else if ((action == "ESCAPE") &&
00515                  (m_fullPreviewShowing))
00516         {
00517             toggleFullscreenPreview();
00518         }
00519         else
00520             handled = false;
00521     }
00522 
00523     if (!handled && MythScreenType::keyPressEvent(event))
00524         handled = true;
00525 
00526     return handled;
00527 }
00528 
00529 void ThemeChooser::toggleFullscreenPreview(void)
00530 {
00531     if (m_fullPreviewStateType)
00532     {
00533         if (m_fullPreviewShowing)
00534         {
00535             if (m_fullScreenPreview)
00536                 m_fullScreenPreview->Reset();
00537 
00538             if (m_fullScreenName)
00539                 m_fullScreenName->Reset();
00540 
00541             m_fullPreviewStateType->Reset();
00542             m_fullPreviewShowing = false;
00543         }
00544         else
00545         {
00546             MythUIButtonListItem *item = m_themes->GetItemCurrent();
00547             ThemeInfo *info = qVariantValue<ThemeInfo*>(item->GetData());
00548             if (info)
00549             {
00550                 if (m_fullScreenPreview)
00551                 {
00552                     m_fullScreenPreview->SetFilename(info->GetPreviewPath());
00553                     m_fullScreenPreview->Load();
00554                 }
00555 
00556                 if (m_fullScreenName)
00557                     m_fullScreenName->SetText(info->GetName());
00558 
00559                 m_fullPreviewStateType->DisplayState("fullscreen");
00560                 m_fullPreviewShowing = true;
00561             }
00562         }
00563     }
00564 }
00565 
00566 void ThemeChooser::toggleThemeUpdateNotifications(void)
00567 {
00568     if (gCoreContext->GetNumSetting("ThemeUpdateNofications", 1))
00569         gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "0", "");
00570     else
00571         gCoreContext->SaveSettingOnHost("ThemeUpdateNofications", "1", "");
00572 }
00573 
00574 void ThemeChooser::refreshDownloadableThemes(void)
00575 {
00576     LOG(VB_GUI, LOG_INFO, LOC + "Forcing remote theme list refresh");
00577     m_refreshDownloadableThemes = true;
00578     gCoreContext->SaveSetting("ThemeInfoDownloadFailures", 0);
00579     ReloadInBackground();
00580 }
00581 
00582 void ThemeChooser::saveAndReload(void)
00583 {
00584     MythUIButtonListItem *current = m_themes->GetItemCurrent();
00585     if (current)
00586         saveAndReload(current);
00587 }
00588 
00589 void ThemeChooser::saveAndReload(MythUIButtonListItem *item)
00590 {
00591     ThemeInfo *info = qVariantValue<ThemeInfo *>(item->GetData());
00592 
00593     if (!info)
00594         return;
00595 
00596     if (!info->GetDownloadURL().isEmpty())
00597     {
00598         QString downloadURL = info->GetDownloadURL();
00599         QFileInfo qfile(downloadURL);
00600         QString baseName = qfile.fileName();
00601 
00602         if (!gCoreContext->GetSetting("ThemeDownloadURL").isEmpty())
00603         {
00604             QStringList tokens =
00605                 gCoreContext->GetSetting("ThemeDownloadURL")
00606                     .split(";", QString::SkipEmptyParts);
00607             QString origURL = downloadURL;
00608             downloadURL.replace(tokens[0], tokens[1]);
00609             LOG(VB_FILE, LOG_WARNING, LOC +
00610                 QString("Theme download URL overridden from %1 to %2.")
00611                     .arg(origURL).arg(downloadURL));
00612         }
00613 
00614         OpenBusyPopup(tr("Downloading %1 Theme").arg(info->GetName()));
00615         m_downloadTheme = info;
00616 #if 0
00617         m_downloadFile = RemoteDownloadFile(downloadURL,
00618                                             "Temp", baseName);
00619         m_downloadState = dsDownloadingOnBackend;
00620 #else
00621         QString localFile = GetConfDir() + "/tmp/" + baseName;
00622         GetMythDownloadManager()->queueDownload(downloadURL, localFile, this);
00623         m_downloadFile = localFile;
00624         m_downloadState = dsDownloadingOnFrontend;
00625 #endif
00626     }
00627     else
00628     {
00629         gCoreContext->SaveSetting("Theme", info->GetDirectoryName());
00630         GetMythMainWindow()->JumpTo("Reload Theme");
00631     }
00632 }
00633 
00634 void ThemeChooser::itemChanged(MythUIButtonListItem *item)
00635 {
00636     ThemeInfo *info = qVariantValue<ThemeInfo*>(item->GetData());
00637 
00638     if (!info)
00639         return;
00640 
00641     QFileInfo preview(info->GetPreviewPath());
00642     QHash<QString, QString> infomap;
00643     info->ToMap(infomap);
00644     SetTextFromMap(infomap);
00645     if (m_preview)
00646     {
00647         if (preview.exists())
00648         {
00649             m_preview->SetFilename(info->GetPreviewPath());
00650             m_preview->Load();
00651         }
00652         else
00653             m_preview->Reset();
00654     }
00655     if (m_fullPreviewShowing && m_fullPreviewStateType)
00656     {
00657         if (m_fullScreenPreview)
00658         {
00659             if (preview.exists())
00660             {
00661                 m_fullScreenPreview->SetFilename(info->GetPreviewPath());
00662                 m_fullScreenPreview->Load();
00663             }
00664             else
00665                 m_fullScreenPreview->Reset();
00666         }
00667 
00668         if (m_fullScreenName)
00669             m_fullScreenName->SetText(info->GetName());
00670     }
00671 
00672     MythUIStateType *themeLocation =
00673                     dynamic_cast<MythUIStateType*>(GetChild("themelocation"));
00674     if (themeLocation)
00675     {
00676         if (info->GetDownloadURL().isEmpty())
00677             themeLocation->DisplayState("local");
00678         else
00679             themeLocation->DisplayState("remote");
00680     }
00681 
00682     MythUIStateType *aspectState =
00683                         dynamic_cast<MythUIStateType*>(GetChild("aspectstate"));
00684     if (aspectState)
00685         aspectState->DisplayState(info->GetAspect());
00686 }
00687 
00688 void ThemeChooser::updateProgressBar(int bytesReceived,
00689                                      int bytesTotal)
00690 {
00691     MythUIProgressBar *progressBar   =
00692         dynamic_cast<MythUIProgressBar *>(GetChild("downloadprogressbar"));
00693 
00694     if (!progressBar)
00695         return;
00696 
00697     progressBar->SetUsed(bytesReceived);
00698     progressBar->SetTotal(bytesTotal);
00699 }
00700 
00701 void ThemeChooser::customEvent(QEvent *e)
00702 {
00703     if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
00704     {
00705         MythEvent *me = (MythEvent *)e;
00706         QStringList tokens = me->Message().split(" ", QString::SkipEmptyParts);
00707 
00708         if (tokens.isEmpty())
00709             return;
00710 
00711         if (tokens[0] == "DOWNLOAD_FILE")
00712         {
00713             QStringList args = me->ExtraDataList();
00714             if ((m_downloadState == dsIdle) ||
00715                 (tokens.size() != 2) ||
00716                 (!m_downloadTheme) ||
00717                 (args[1] != m_downloadFile))
00718                 return;
00719 
00720             if (tokens[1] == "UPDATE")
00721             {
00722                 updateProgressBar(args[2].toInt(), args[3].toInt());
00723             }
00724             else if (tokens[1] == "FINISHED")
00725             {
00726                 bool remoteFileIsLocal = false;
00727                 int fileSize  = args[2].toInt();
00728                 int errorCode = args[4].toInt();
00729 
00730                 CloseBusyPopup();
00731 
00732                 QFileInfo file(m_downloadFile);
00733                 if ((m_downloadState == dsDownloadingOnBackend) &&
00734                     (m_downloadFile.startsWith("myth://")))
00735                 {
00736                     // The backend download is finished so start the
00737                     // frontend download
00738                     if ((errorCode == 0) &&
00739                         (fileSize > 0))
00740                     {
00741                         m_downloadState = dsDownloadingOnFrontend;
00742                         QString localFile = GetConfDir() + "/tmp/" +
00743                             file.fileName();
00744                         file.setFile(localFile);
00745 
00746                         if (file.exists())
00747                         {
00748                             remoteFileIsLocal = true;
00749                             m_downloadFile = localFile;
00750                         }
00751                         else
00752                         {
00753                             GetMythDownloadManager()->queueDownload(
00754                                 m_downloadFile, localFile, this);
00755                             OpenBusyPopup(tr("Copying %1 Theme Package")
00756                                           .arg(m_downloadTheme->GetName()));
00757                             m_downloadFile = localFile;
00758                             return;
00759                         }
00760                     }
00761                     else
00762                     {
00763                         m_downloadState = dsIdle;
00764                         ShowOkPopup(tr("ERROR downloading theme package on master backend."));
00765                     }
00766                 }
00767 
00768                 if ((m_downloadState == dsDownloadingOnFrontend) &&
00769                     (file.exists()))
00770                 {
00771                     // The frontend download is finished
00772                     if ((errorCode == 0) &&
00773                         (fileSize > 0))
00774                     {
00775                         m_downloadState = dsExtractingTheme;
00776                         ThemeExtractThread *extractThread =
00777                             new ThemeExtractThread(this, m_downloadFile,
00778                                                    m_userThemeDir);
00779                         MThreadPool::globalInstance()->start(
00780                             extractThread, "ThemeExtract");
00781 
00782                         if (!remoteFileIsLocal)
00783                             RemoteFile::DeleteFile(args[0]);
00784 
00785                         OpenBusyPopup(tr("Installing %1 Theme")
00786                                       .arg(m_downloadTheme->GetName()));
00787                     }
00788                     else
00789                     {
00790                         m_downloadState = dsIdle;
00791                         ShowOkPopup(tr("ERROR downloading theme package from master backend."));
00792                     }
00793                 }
00794             }
00795         }
00796         else if ((me->Message() == "THEME_INSTALLED") &&
00797                  (m_downloadTheme) &&
00798                  (m_downloadState == dsExtractingTheme))
00799         {
00800             m_downloadState = dsIdle;
00801             CloseBusyPopup();
00802             QStringList args = me->ExtraDataList();
00803             QFile::remove(args[0]);
00804 
00805             QString event = QString("THEME_INSTALLED PATH %1")
00806                                     .arg(m_userThemeDir +
00807                                          m_downloadTheme->GetDirectoryName());
00808             gCoreContext->SendSystemEvent(event);
00809 
00810             gCoreContext->SaveSetting("Theme", m_downloadTheme->GetDirectoryName());
00811             GetMythMainWindow()->JumpTo("Reload Theme");
00812         }
00813     }
00814 }
00815 
00816 void ThemeChooser::removeTheme(void)
00817 {
00818     MythUIButtonListItem *current = m_themes->GetItemCurrent();
00819     if (!current)
00820     {
00821         ShowOkPopup(tr("Error, no theme selected."));
00822         return;
00823     }
00824 
00825     ThemeInfo *info = qVariantValue<ThemeInfo *>(current->GetData());
00826     if (!info)
00827     {
00828         ShowOkPopup(tr("Error, unable to find current theme."));
00829         return;
00830     }
00831 
00832     if (!info->GetPreviewPath().startsWith(m_userThemeDir))
00833     {
00834         ShowOkPopup(tr("%1 is not a user-installed theme and can not "
00835                        "be deleted.").arg(info->GetName()));
00836         return;
00837     }
00838 
00839     removeThemeDir(m_userThemeDir + info->GetDirectoryName());
00840 
00841     ReloadInBackground();
00842 }
00843 
00844 void ThemeChooser::removeThemeDir(const QString &dirname)
00845 {
00846     if ((!dirname.startsWith(m_userThemeDir)) &&
00847         (!dirname.startsWith(GetMythUI()->GetThemeCacheDir())))
00848         return;
00849 
00850     QDir dir(dirname);
00851 
00852     if (!dir.exists())
00853         return;
00854 
00855     QFileInfoList list = dir.entryInfoList();
00856     QFileInfoList::const_iterator it = list.begin();
00857     const QFileInfo *fi;
00858 
00859     while (it != list.end())
00860     {
00861         fi = &(*it++);
00862         if (fi->fileName() == "." || fi->fileName() == "..")
00863             continue;
00864         if (fi->isFile() && !fi->isSymLink())
00865         {
00866             QFile::remove(fi->absoluteFilePath());
00867         }
00868         else if (fi->isDir() && !fi->isSymLink())
00869         {
00870             removeThemeDir(fi->absoluteFilePath());
00871         }
00872     }
00873 
00874     dir.rmdir(dirname);
00875 }
00876 
00878 
00879 ThemeUpdateChecker::ThemeUpdateChecker() :
00880     m_updateTimer(new QTimer(this))
00881 {
00882     m_mythVersion = MYTH_SOURCE_PATH;
00883 
00884     // FIXME: For now, treat git master the same as svn trunk
00885     if (m_mythVersion == "master")
00886         m_mythVersion = "trunk";
00887 
00888     if (m_mythVersion != "trunk")
00889     {
00890         m_mythVersion = MYTH_BINARY_VERSION; // Example: 0.25.20101017-1
00891         m_mythVersion.replace(QRegExp("\\.[0-9]{8,}.*"), "");
00892     }
00893 
00894     m_infoPackage = gCoreContext->GenMythURL(gCoreContext->GetSetting("MasterServerIP"),
00895                                              0,
00896                                              "remotethemes/themes.zip",
00897                                              "Temp");
00898 
00899     gCoreContext->SaveSetting("ThemeUpdateStatus", "");
00900                                  
00901     connect(m_updateTimer, SIGNAL(timeout()), SLOT(checkForUpdate()));
00902     m_updateTimer->start(60 * 60 * 1000); // Run once an hour
00903 
00904     // Run once 15 seconds from now
00905     QTimer::singleShot(15 * 1000, this, SLOT(checkForUpdate()));
00906 }
00907 
00908 ThemeUpdateChecker::~ThemeUpdateChecker()
00909 {
00910     if (m_updateTimer)
00911     {
00912         m_updateTimer->stop();
00913         delete m_updateTimer;
00914         m_updateTimer = NULL;
00915     }
00916 }
00917 
00918 void ThemeUpdateChecker::checkForUpdate(void)
00919 {
00920     if (GetMythUI()->GetCurrentLocation(false, true) != "mainmenu")
00921         return;
00922 
00923     if (RemoteFile::Exists(m_infoPackage))
00924     {
00925         QString remoteThemeDir =
00926             gCoreContext->GenMythURL(gCoreContext->GetSetting("MasterServerIP"),
00927                                      0,
00928                                      QString("remotethemes/%1/%2")
00929                                              .arg(m_mythVersion)
00930                                              .arg(GetMythUI()->GetThemeName()),
00931                                      "Temp");
00932 
00933         QString infoXML = remoteThemeDir;
00934         infoXML.append("/themeinfo.xml");
00935 
00936         if (RemoteFile::Exists(infoXML))
00937         {
00938             ThemeInfo *remoteTheme = new ThemeInfo(remoteThemeDir);
00939             if (!remoteTheme)
00940             {
00941                 LOG(VB_GENERAL, LOG_ERR,
00942                     QString("ThemeUpdateChecker::checkForUpdate(): "
00943                             "Unable to create ThemeInfo for %1")
00944                         .arg(infoXML));
00945                 return;
00946             }
00947                 
00948             ThemeInfo *localTheme = new ThemeInfo(GetMythUI()->GetThemeDir());
00949             if (!localTheme)
00950             {
00951                 LOG(VB_GENERAL, LOG_ERR,
00952                     "ThemeUpdateChecker::checkForUpdate(): " 
00953                     "Unable to create ThemeInfo for current theme");
00954                 return;
00955             }
00956 
00957             int rmtMaj = remoteTheme->GetMajorVersion();
00958             int rmtMin = remoteTheme->GetMinorVersion();
00959             int locMaj = localTheme->GetMajorVersion();
00960             int locMin = localTheme->GetMinorVersion();
00961 
00962             if ((rmtMaj > locMaj) ||
00963                 ((rmtMaj == locMaj) &&
00964                  (rmtMin > locMin)))
00965             {
00966                 m_lastKnownThemeVersion =
00967                     QString("%1-%2.%3").arg(GetMythUI()->GetThemeName())
00968                                        .arg(rmtMaj).arg(rmtMin);
00969 
00970                 QString status = gCoreContext->GetSetting("ThemeUpdateStatus");
00971                 QString currentLocation = GetMythUI()->GetCurrentLocation(false, true);
00972 
00973                 if ((!status.startsWith(m_lastKnownThemeVersion)) &&
00974                     (currentLocation == "mainmenu"))
00975                 {
00976                     m_currentVersion = QString("%1.%2").arg(locMaj).arg(locMin);
00977                     m_newVersion = QString("%1.%2").arg(rmtMaj).arg(rmtMin);
00978 
00979                     gCoreContext->SaveSetting("ThemeUpdateStatus",
00980                                          m_lastKnownThemeVersion + " notified");
00981 
00982                     QString message = tr("Version %1 of the %2 theme is now "
00983                                          "available in the Theme Chooser.  The "
00984                                          "currently installed version is %3.")
00985                                          .arg(m_newVersion)
00986                                          .arg(GetMythUI()->GetThemeName())
00987                                          .arg(m_currentVersion);
00988 
00989                     ShowOkPopup(message);
00990                 }
00991             }
00992                 
00993             delete remoteTheme;
00994             delete localTheme;
00995         }
00996     }
00997 }
00998 
00999 /* vim: set expandtab tabstop=4 shiftwidth=4: */
01000 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends