• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KNewStuff

engine.cpp

Go to the documentation of this file.
00001 /*
00002     knewstuff3/engine.cpp
00003     Copyright (c) 2007 Josef Spillner <spillner@kde.org>
00004     Copyright (C) 2007-2010 Frederik Gladhorn <gladhorn@kde.org>
00005     Copyright (c) 2009 Jeremy Whiting <jpwhiting@kde.org>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Lesser General Public
00009     License as published by the Free Software Foundation; either
00010     version 2.1 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Lesser General Public License for more details.
00016 
00017     You should have received a copy of the GNU Lesser General Public
00018     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
00019 */
00020 
00021 #include "engine.h"
00022 
00023 #include "entry.h"
00024 #include "core/installation.h"
00025 #include "core/xmlloader.h"
00026 #include "ui/imageloader.h"
00027 
00028 #include <kaboutdata.h>
00029 #include <kconfig.h>
00030 #include <kconfiggroup.h>
00031 #include <kcomponentdata.h>
00032 #include <kdebug.h>
00033 #include <kstandarddirs.h>
00034 #include <kcodecs.h>
00035 #include <kprocess.h>
00036 #include <kshell.h>
00037 
00038 #include <kio/job.h>
00039 #include <kmimetype.h>
00040 #include <krandom.h>
00041 #include <ktoolinvocation.h>
00042 
00043 #include <QtCore/QTimer>
00044 #include <QtCore/QDir>
00045 #include <QtXml/qdom.h>
00046 #include <QtCore/Q_PID>
00047 
00048 #if defined(Q_OS_WIN)
00049 #include <windows.h>
00050 #define _WIN32_IE 0x0500
00051 #include <shlobj.h>
00052 #endif
00053 
00054 // libattica
00055 #include <attica/providermanager.h>
00056 
00057 // own
00058 #include "attica/atticaprovider.h"
00059 #include "core/cache.h"
00060 #include "staticxml/staticxmlprovider.h"
00061 
00062 using namespace KNS3;
00063 
00064 Engine::Engine(QObject* parent)
00065     : QObject(parent)
00066     , m_initialized(false)
00067     , m_installation(new Installation)
00068     , m_cache(new Cache)
00069     , m_searchTimer(new QTimer)
00070     , m_currentPage(-1)
00071     , m_pageSize(20)
00072     , m_numDataJobs(0)
00073     , m_numPictureJobs(0)
00074     , m_numInstallJobs(0)
00075     , m_atticaProviderManager(0)
00076 {
00077     m_searchTimer->setSingleShot(true);
00078     m_searchTimer->setInterval(1000);
00079     connect(m_searchTimer, SIGNAL(timeout()), SLOT(slotSearchTimerExpired()));
00080     connect(this, SIGNAL(signalEntryChanged(const KNS3::EntryInternal&)), m_cache, SLOT(registerChangedEntry(const KNS3::EntryInternal&)));
00081     connect(m_installation, SIGNAL(signalInstallationFinished()), this, SLOT(slotInstallationFinished()));
00082     connect(m_installation, SIGNAL(signalInstallationFailed(QString)), this, SLOT(slotInstallationFailed(QString)));
00083 
00084 }
00085 
00086 Engine::~Engine()
00087 {
00088     m_cache->writeRegistry();
00089     delete m_atticaProviderManager;
00090     delete m_searchTimer;
00091     delete m_installation;
00092     delete m_cache;
00093 }
00094 
00095 bool Engine::init(const QString &configfile)
00096 {
00097     kDebug() << "Initializing KNS3::Engine from '" << configfile << "'";
00098 
00099     emit signalBusy(i18n("Initializing"));
00100 
00101     KConfig conf(configfile);
00102     if (conf.accessMode() == KConfig::NoAccess) {
00103         emit signalError(i18n("Configuration file not found: \"%1\"", configfile));
00104         kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00105         return false;
00106     }
00107     // FIXME: accessMode() doesn't return NoAccess for non-existing files
00108     // - bug in kdecore?
00109     // - this needs to be looked at again until KConfig backend changes for KDE 4
00110     // the check below is a workaround
00111     if (KStandardDirs::locate("config", configfile).isEmpty()) {
00112         emit signalError(i18n("Configuration file not found: \"%1\"", configfile));
00113         kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00114         return false;
00115     }
00116 
00117     KConfigGroup group;
00118     if (conf.hasGroup("KNewStuff3")) {
00119         kDebug() << "Loading KNewStuff3 config: " << configfile;
00120         group = conf.group("KNewStuff3");
00121     } else if (conf.hasGroup("KNewStuff2")) {
00122         kDebug() << "Loading KNewStuff2 config: " << configfile;
00123         group = conf.group("KNewStuff2");
00124     } else {
00125         emit signalError(i18n("Configuration file is invalid: \"%1\"", configfile));
00126         kError() << "A knsrc file was found but it doesn't contain a KNewStuff3 section." << endl;
00127         return false;
00128     }
00129 
00130     m_categories = group.readEntry("Categories", QStringList());
00131 
00132     kDebug() << "Categories: " << m_categories;
00133     m_providerFileUrl = group.readEntry("ProvidersUrl", QString());
00134     m_applicationName = QFileInfo(KStandardDirs::locate("config", configfile)).baseName() + ':';
00135 
00136     // let installation read install specific config
00137     if (!m_installation->readConfig(group)) {
00138         return false;
00139     }
00140 
00141     connect(m_installation, SIGNAL(signalEntryChanged(const KNS3::EntryInternal&)), SLOT(slotEntryChanged(const KNS3::EntryInternal&)));
00142 
00143     m_cache->setRegistryFileName(m_applicationName.split(':')[0]);
00144     m_cache->readRegistry();
00145 
00146     m_initialized = true;
00147 
00148     // load the providers
00149     loadProviders();
00150 
00151     return true;
00152 }
00153 
00154 QStringList Engine::categories() const
00155 {
00156     return m_categories;
00157 }
00158 
00159 QStringList Engine::categoriesFilter() const
00160 {
00161     return m_currentRequest.categories;
00162 }
00163 
00164 void Engine::loadProviders()
00165 {
00166     if (m_providerFileUrl.isEmpty()) {
00167         // it would be nicer to move the attica stuff into its own class
00168         kDebug(550) << "Using OCS default providers";
00169         Attica::ProviderManager* m_atticaProviderManager = new Attica::ProviderManager;
00170         connect(m_atticaProviderManager, SIGNAL(providerAdded(Attica::Provider)), this, SLOT(atticaProviderLoaded(Attica::Provider)));
00171         m_atticaProviderManager->loadDefaultProviders();
00172     } else {
00173         kDebug(550) << "loading providers from " << m_providerFileUrl;
00174         emit signalBusy(i18n("Loading provider information"));
00175 
00176         XmlLoader * loader = new XmlLoader(this);
00177         connect(loader, SIGNAL(signalLoaded(const QDomDocument&)), SLOT(slotProviderFileLoaded(const QDomDocument&)));
00178         connect(loader, SIGNAL(signalFailed()), SLOT(slotProvidersFailed()));
00179 
00180         loader->load(KUrl(m_providerFileUrl));
00181     }
00182 }
00183 
00184 void Engine::slotProviderFileLoaded(const QDomDocument& doc)
00185 {
00186     kDebug() << "slotProvidersLoaded";
00187 
00188     bool isAtticaProviderFile = false;
00189 
00190     // get each provider element, and create a provider object from it
00191     QDomElement providers = doc.documentElement();
00192 
00193     if (providers.tagName() == "providers") {
00194         isAtticaProviderFile = true;
00195     } else if (providers.tagName() != "ghnsproviders" && providers.tagName() != "knewstuffproviders") {
00196         kWarning(550) << "No document in providers.xml.";
00197         emit signalError(i18n("Could not load get hot new stuff providers from file: %1", m_providerFileUrl));
00198         return;
00199     }
00200 
00201     QDomElement n = providers.firstChildElement("provider");
00202     while (!n.isNull()) {
00203         kDebug() << "Provider attributes: " << n.attribute("type");
00204 
00205         QSharedPointer<KNS3::Provider> provider;
00206         if (isAtticaProviderFile || n.attribute("type").toLower() == "rest") {
00207             provider = QSharedPointer<KNS3::Provider> (new AtticaProvider(m_categories));
00208         } else {
00209             provider = QSharedPointer<KNS3::Provider> (new StaticXmlProvider);
00210         }
00211 
00212         if (provider->setProviderXML(n)) {
00213             addProvider(provider);
00214         } else {
00215             emit signalError(i18n("Error initializing provider."));
00216         }
00217         n = n.nextSiblingElement();
00218     }
00219     emit signalBusy(i18n("Loading data"));
00220 }
00221 
00222 void Engine::atticaProviderLoaded(const Attica::Provider& atticaProvider)
00223 {
00224     if (!atticaProvider.hasContentService()) {
00225         kDebug() << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content"; 
00226         return;
00227     }
00228     QSharedPointer<KNS3::Provider> provider =
00229             QSharedPointer<KNS3::Provider> (new AtticaProvider(atticaProvider, m_categories));
00230     addProvider(provider);
00231 }
00232 
00233 void Engine::addProvider(QSharedPointer<KNS3::Provider> provider)
00234 {
00235     m_providers.insert(provider->id(), provider);
00236     connect(provider.data(), SIGNAL(providerInitialized(KNS3::Provider*)), SLOT(providerInitialized(KNS3::Provider*)));
00237     connect(provider.data(), SIGNAL(loadingFinished(KNS3::Provider::SearchRequest, KNS3::EntryInternal::List)),
00238             SLOT(slotEntriesLoaded(KNS3::Provider::SearchRequest, KNS3::EntryInternal::List)));
00239     connect(provider.data(), SIGNAL(entryDetailsLoaded(KNS3::EntryInternal)), SLOT(slotEntryDetailsLoaded(KNS3::EntryInternal)));
00240     connect(provider.data(), SIGNAL(payloadLinkLoaded(const KNS3::EntryInternal&)), SLOT(downloadLinkLoaded(const KNS3::EntryInternal&)));
00241     connect(provider.data(), SIGNAL(signalError(QString)), this, SIGNAL(signalError(QString)));
00242     connect(provider.data(), SIGNAL(signalInformation(QString)), this, SIGNAL(signalIdle(QString)));
00243 }
00244 
00245 void Engine::providerJobStarted ( KJob* job )
00246 {
00247     emit jobStarted(job, i18n("Loading data from provider"));
00248 }
00249 
00250 void Engine::slotProvidersFailed()
00251 {
00252     emit signalError(i18n("Loading of providers from file: %1 failed", m_providerFileUrl));
00253 }
00254 
00255 void Engine::providerInitialized(Provider* p)
00256 {
00257     kDebug() << "providerInitialized" << p->name();
00258     p->setCachedEntries(m_cache->registryForProvider(p->id()));
00259     updateStatus();
00260 
00261     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00262         if (!p->isInitialized()) {
00263             return;
00264         }
00265     }
00266     emit signalProvidersLoaded();
00267 }
00268 
00269 void Engine::slotEntriesLoaded(const KNS3::Provider::SearchRequest& request, KNS3::EntryInternal::List entries)
00270 {
00271     m_currentPage = qMax<int>(request.page, m_currentPage);
00272     kDebug() << "loaded page " << request.page << "current page" << m_currentPage;
00273 
00274     if (request.sortMode == Provider::Updates) {
00275         emit signalUpdateableEntriesLoaded(entries);
00276     } else {
00277         m_cache->insertRequest(request, entries);
00278         emit signalEntriesLoaded(entries);
00279     }
00280     
00281     --m_numDataJobs;
00282     updateStatus();
00283 }
00284 
00285 void Engine::reloadEntries()
00286 {
00287     emit signalResetView();
00288     m_currentPage = -1;
00289     m_currentRequest.page = 0;
00290     m_numDataJobs = 0;
00291 
00292     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00293         if (p->isInitialized()) {
00294             if (m_currentRequest.sortMode == Provider::Installed) {
00295                 // when asking for installed entries, never use the cache
00296                 p->loadEntries(m_currentRequest);
00297             } else {
00298                 // take entries from cache until there are no more
00299                 EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
00300                 while (!cache.isEmpty()) {
00301                     kDebug() << "From cache";
00302                     emit signalEntriesLoaded(cache);
00303 
00304                     m_currentPage = m_currentRequest.page;
00305                     ++m_currentRequest.page;
00306                     cache = m_cache->requestFromCache(m_currentRequest);
00307                 }
00308                 // if the cache was empty, request data from provider
00309                 if (m_currentPage == -1) {
00310                     kDebug() << "From provider";
00311                     p->loadEntries(m_currentRequest);
00312 
00313                     ++m_numDataJobs;
00314                     updateStatus();
00315                 }
00316             }
00317         }
00318     }
00319 }
00320 
00321 void Engine::setCategoriesFilter(const QStringList& categories)
00322 {
00323     m_currentRequest.categories = categories;
00324     reloadEntries();
00325 }
00326 
00327 void Engine::setSortMode(Provider::SortMode mode)
00328 {
00329     if (m_currentRequest.sortMode != mode) {
00330         m_currentRequest.page = -1;
00331     }
00332     m_currentRequest.sortMode = mode;
00333     reloadEntries();
00334 }
00335 
00336 void Engine::setSearchTerm(const QString& searchString)
00337 {
00338     m_searchTimer->stop();
00339     m_currentRequest.searchTerm = searchString;
00340     EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
00341     if (!cache.isEmpty()) {
00342         reloadEntries();
00343     } else {
00344         m_searchTimer->start();
00345     }
00346 }
00347 
00348 void Engine::slotSearchTimerExpired()
00349 {
00350     reloadEntries();
00351 }
00352 
00353 void Engine::requestMoreData()
00354 {
00355     kDebug() << "Get more data! current page: " << m_currentPage  << " requested: " << m_currentRequest.page;
00356 
00357     if (m_currentPage < m_currentRequest.page) {
00358         return;
00359     }
00360 
00361     m_currentRequest.page++;
00362     doRequest();
00363 }
00364 
00365 void Engine::requestData(int page, int pageSize)
00366 {
00367     m_currentRequest.page = page;
00368     m_currentRequest.pageSize = pageSize;
00369     doRequest();
00370 }
00371 
00372 void Engine::doRequest()
00373 {
00374     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00375         if (p->isInitialized()) {
00376             p->loadEntries(m_currentRequest);
00377             ++m_numDataJobs;
00378             updateStatus();
00379         }
00380     }
00381 }
00382 
00383 void Engine::install(KNS3::EntryInternal entry, int linkId)
00384 {
00385     if (entry.status() == Entry::Updateable) {
00386         entry.setStatus(Entry::Updating);
00387     } else  {
00388         entry.setStatus(Entry::Installing);
00389     }
00390     emit signalEntryChanged(entry);
00391 
00392     kDebug() << "Install " << entry.name()
00393         << " from: " << entry.providerId();
00394     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00395     if (p) {
00396         p->loadPayloadLink(entry, linkId);
00397 
00398         ++m_numInstallJobs;
00399         updateStatus();
00400     }
00401 }
00402 
00403 void Engine::slotInstallationFinished()
00404 {
00405     --m_numInstallJobs;
00406     updateStatus();
00407 }
00408 
00409 void Engine::slotInstallationFailed(const QString& message)
00410 {
00411     --m_numInstallJobs;
00412     emit signalError(message);
00413 }
00414 
00415 void Engine::slotEntryDetailsLoaded(const KNS3::EntryInternal& entry)
00416 {
00417     emit signalEntryDetailsLoaded(entry);
00418 }
00419 
00420 void Engine::downloadLinkLoaded(const KNS3::EntryInternal& entry)
00421 {
00422     m_installation->install(entry);
00423 }
00424 
00425 void Engine::uninstall(KNS3::EntryInternal entry)
00426 {
00427     // FIXME: change the status?
00428     entry.setStatus(Entry::Installing);
00429     emit signalEntryChanged(entry);
00430     m_installation->uninstall(entry);
00431 }
00432 
00433 void Engine::loadDetails(const KNS3::EntryInternal &entry)
00434 {
00435     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00436     p->loadEntryDetails(entry);
00437 }
00438 
00439 void Engine::loadPreview(const KNS3::EntryInternal& entry, EntryInternal::PreviewType type)
00440 {
00441     kDebug() << "START  preview: " << entry.name() << type;
00442     ImageLoader* l = new ImageLoader(entry, type, this);
00443     connect(l, SIGNAL(signalPreviewLoaded(KNS3::EntryInternal,KNS3::EntryInternal::PreviewType)), this, SLOT(slotPreviewLoaded(KNS3::EntryInternal,KNS3::EntryInternal::PreviewType)));
00444     l->start();
00445     ++m_numPictureJobs;
00446     updateStatus();
00447 }
00448 
00449 void Engine::slotPreviewLoaded(const KNS3::EntryInternal& entry, EntryInternal::PreviewType type)
00450 {
00451     kDebug() << "FINISH preview: " << entry.name() << type;
00452     emit signalEntryPreviewLoaded(entry, type);
00453     --m_numPictureJobs;
00454     updateStatus();
00455 }
00456 
00457 void Engine::contactAuthor(const EntryInternal &entry)
00458 {
00459     if (!entry.author().email().isEmpty()) {
00460         // invoke mail with the address of the author
00461         KToolInvocation::invokeMailer(entry.author().email(), i18n("Re: %1", entry.name()));
00462     } else if (!entry.author().homepage().isEmpty()) {
00463         KToolInvocation::invokeBrowser(entry.author().homepage());
00464     }
00465 }
00466 
00467 void Engine::slotEntryChanged(const KNS3::EntryInternal& entry)
00468 {
00469     emit signalEntryChanged(entry);
00470 }
00471 
00472 bool Engine::userCanVote(const EntryInternal& entry)
00473 {
00474     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00475     return p->userCanVote();
00476 }
00477 
00478 void Engine::vote(const EntryInternal& entry, uint rating)
00479 {
00480     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00481     p->vote(entry, rating);
00482 }
00483 
00484 bool Engine::userCanBecomeFan(const EntryInternal& entry)
00485 {
00486     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00487     return p->userCanBecomeFan();
00488 }
00489 
00490 void Engine::becomeFan(const EntryInternal& entry)
00491 {
00492     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00493     p->becomeFan(entry);
00494 }
00495 
00496 void Engine::updateStatus()
00497 {
00498     if (m_numDataJobs > 0) {
00499         emit signalBusy(i18n("Loading data"));
00500     } else if (m_numPictureJobs > 0) {
00501         emit signalBusy(i18np("Loading one preview", "Loading %1 previews", m_numPictureJobs));
00502     } else if (m_numInstallJobs > 0) {
00503         emit signalBusy(i18n("Installing"));
00504     } else {
00505         emit signalIdle(QString());
00506     }
00507 }
00508 
00509 void Engine::checkForUpdates()
00510 {
00511     foreach(QSharedPointer<Provider> p, m_providers) {
00512         Provider::SearchRequest request(KNS3::Provider::Updates);
00513         p->loadEntries(request);
00514     }
00515 }
00516 
00517 #include "engine.moc"

KNewStuff

Skip menu "KNewStuff"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal