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