|
MythTV
0.26-pre
|
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
1.7.6.1