• Skip to content
  • Skip to link menu
KDE 4.7 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_cache(0)
00070     , m_searchTimer(new QTimer)
00071     , m_currentPage(-1)
00072     , m_pageSize(20)
00073     , m_numDataJobs(0)
00074     , m_numPictureJobs(0)
00075     , m_numInstallJobs(0)
00076     , m_atticaProviderManager(0)
00077 {
00078     m_searchTimer->setSingleShot(true);
00079     m_searchTimer->setInterval(1000);
00080     connect(m_searchTimer, SIGNAL(timeout()), SLOT(slotSearchTimerExpired()));
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     if (m_cache) {
00089       m_cache->writeRegistry();
00090     }
00091     delete m_atticaProviderManager;
00092     delete m_searchTimer;
00093     delete m_installation;
00094 }
00095 
00096 bool Engine::init(const QString &configfile)
00097 {
00098     kDebug() << "Initializing KNS3::Engine from '" << configfile << "'";
00099 
00100     emit signalBusy(i18n("Initializing"));
00101 
00102     KConfig conf(configfile);
00103     if (conf.accessMode() == KConfig::NoAccess) {
00104         emit signalError(i18n("Configuration file not found: \"%1\"", configfile));
00105         kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00106         return false;
00107     }
00108     // FIXME: accessMode() doesn't return NoAccess for non-existing files
00109     // - bug in kdecore?
00110     // - this needs to be looked at again until KConfig backend changes for KDE 4
00111     // the check below is a workaround
00112     if (KStandardDirs::locate("config", configfile).isEmpty()) {
00113         emit signalError(i18n("Configuration file not found: \"%1\"", configfile));
00114         kError() << "No knsrc file named '" << configfile << "' was found." << endl;
00115         return false;
00116     }
00117 
00118     KConfigGroup group;
00119     if (conf.hasGroup("KNewStuff3")) {
00120         kDebug() << "Loading KNewStuff3 config: " << configfile;
00121         group = conf.group("KNewStuff3");
00122     } else if (conf.hasGroup("KNewStuff2")) {
00123         kDebug() << "Loading KNewStuff2 config: " << configfile;
00124         group = conf.group("KNewStuff2");
00125     } else {
00126         emit signalError(i18n("Configuration file is invalid: \"%1\"", configfile));
00127         kError() << "A knsrc file was found but it doesn't contain a KNewStuff3 section." << endl;
00128         return false;
00129     }
00130 
00131     m_categories = group.readEntry("Categories", QStringList());
00132 
00133     kDebug() << "Categories: " << m_categories;
00134     m_providerFileUrl = group.readEntry("ProvidersUrl", QString());
00135     m_applicationName = QFileInfo(KStandardDirs::locate("config", configfile)).baseName() + ':';
00136 
00137     // let installation read install specific config
00138     if (!m_installation->readConfig(group)) {
00139         return false;
00140     }
00141 
00142     connect(m_installation, SIGNAL(signalEntryChanged(const KNS3::EntryInternal&)), SLOT(slotEntryChanged(const KNS3::EntryInternal&)));
00143 
00144     m_cache = Cache::getCache(m_applicationName.split(':')[0]);
00145     connect(this, SIGNAL(signalEntryChanged(const KNS3::EntryInternal&)), m_cache.data(), SLOT(registerChangedEntry(const KNS3::EntryInternal&)));
00146     m_cache->readRegistry();
00147 
00148     m_initialized = true;
00149 
00150     // load the providers
00151     loadProviders();
00152 
00153     return true;
00154 }
00155 
00156 QStringList Engine::categories() const
00157 {
00158     return m_categories;
00159 }
00160 
00161 QStringList Engine::categoriesFilter() const
00162 {
00163     return m_currentRequest.categories;
00164 }
00165 
00166 void Engine::loadProviders()
00167 {
00168     if (m_providerFileUrl.isEmpty()) {
00169         // it would be nicer to move the attica stuff into its own class
00170         kDebug(550) << "Using OCS default providers";
00171         Attica::ProviderManager* m_atticaProviderManager = new Attica::ProviderManager;
00172         connect(m_atticaProviderManager, SIGNAL(providerAdded(Attica::Provider)), this, SLOT(atticaProviderLoaded(Attica::Provider)));
00173         m_atticaProviderManager->loadDefaultProviders();
00174     } else {
00175         kDebug(550) << "loading providers from " << m_providerFileUrl;
00176         emit signalBusy(i18n("Loading provider information"));
00177 
00178         XmlLoader * loader = new XmlLoader(this);
00179         connect(loader, SIGNAL(signalLoaded(const QDomDocument&)), SLOT(slotProviderFileLoaded(const QDomDocument&)));
00180         connect(loader, SIGNAL(signalFailed()), SLOT(slotProvidersFailed()));
00181 
00182         loader->load(KUrl(m_providerFileUrl));
00183     }
00184 }
00185 
00186 void Engine::slotProviderFileLoaded(const QDomDocument& doc)
00187 {
00188     kDebug() << "slotProvidersLoaded";
00189 
00190     bool isAtticaProviderFile = false;
00191 
00192     // get each provider element, and create a provider object from it
00193     QDomElement providers = doc.documentElement();
00194 
00195     if (providers.tagName() == "providers") {
00196         isAtticaProviderFile = true;
00197     } else if (providers.tagName() != "ghnsproviders" && providers.tagName() != "knewstuffproviders") {
00198         kWarning(550) << "No document in providers.xml.";
00199         emit signalError(i18n("Could not load get hot new stuff providers from file: %1", m_providerFileUrl));
00200         return;
00201     }
00202 
00203     QDomElement n = providers.firstChildElement("provider");
00204     while (!n.isNull()) {
00205         kDebug() << "Provider attributes: " << n.attribute("type");
00206 
00207         QSharedPointer<KNS3::Provider> provider;
00208         if (isAtticaProviderFile || n.attribute("type").toLower() == "rest") {
00209             provider = QSharedPointer<KNS3::Provider> (new AtticaProvider(m_categories));
00210         } else {
00211             provider = QSharedPointer<KNS3::Provider> (new StaticXmlProvider);
00212         }
00213 
00214         if (provider->setProviderXML(n)) {
00215             addProvider(provider);
00216         } else {
00217             emit signalError(i18n("Error initializing provider."));
00218         }
00219         n = n.nextSiblingElement();
00220     }
00221     emit signalBusy(i18n("Loading data"));
00222 }
00223 
00224 void Engine::atticaProviderLoaded(const Attica::Provider& atticaProvider)
00225 {
00226     if (!atticaProvider.hasContentService()) {
00227         kDebug() << "Found provider: " << atticaProvider.baseUrl() << " but it does not support content"; 
00228         return;
00229     }
00230     QSharedPointer<KNS3::Provider> provider =
00231             QSharedPointer<KNS3::Provider> (new AtticaProvider(atticaProvider, m_categories));
00232     addProvider(provider);
00233 }
00234 
00235 void Engine::addProvider(QSharedPointer<KNS3::Provider> provider)
00236 {
00237     m_providers.insert(provider->id(), provider);
00238     connect(provider.data(), SIGNAL(providerInitialized(KNS3::Provider*)), SLOT(providerInitialized(KNS3::Provider*)));
00239     connect(provider.data(), SIGNAL(loadingFinished(KNS3::Provider::SearchRequest, KNS3::EntryInternal::List)),
00240             SLOT(slotEntriesLoaded(KNS3::Provider::SearchRequest, KNS3::EntryInternal::List)));
00241     connect(provider.data(), SIGNAL(entryDetailsLoaded(KNS3::EntryInternal)), SLOT(slotEntryDetailsLoaded(KNS3::EntryInternal)));
00242     connect(provider.data(), SIGNAL(payloadLinkLoaded(const KNS3::EntryInternal&)), SLOT(downloadLinkLoaded(const KNS3::EntryInternal&)));
00243     connect(provider.data(), SIGNAL(signalError(QString)), this, SIGNAL(signalError(QString)));
00244     connect(provider.data(), SIGNAL(signalInformation(QString)), this, SIGNAL(signalIdle(QString)));
00245 }
00246 
00247 void Engine::providerJobStarted ( KJob* job )
00248 {
00249     emit jobStarted(job, i18n("Loading data from provider"));
00250 }
00251 
00252 void Engine::slotProvidersFailed()
00253 {
00254     emit signalError(i18n("Loading of providers from file: %1 failed", m_providerFileUrl));
00255 }
00256 
00257 void Engine::providerInitialized(Provider* p)
00258 {
00259     kDebug() << "providerInitialized" << p->name();
00260     p->setCachedEntries(m_cache->registryForProvider(p->id()));
00261     updateStatus();
00262 
00263     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00264         if (!p->isInitialized()) {
00265             return;
00266         }
00267     }
00268     emit signalProvidersLoaded();
00269 }
00270 
00271 void Engine::slotEntriesLoaded(const KNS3::Provider::SearchRequest& request, KNS3::EntryInternal::List entries)
00272 {
00273     m_currentPage = qMax<int>(request.page, m_currentPage);
00274     kDebug() << "loaded page " << request.page << "current page" << m_currentPage;
00275 
00276     if (request.sortMode == Provider::Updates) {
00277         emit signalUpdateableEntriesLoaded(entries);
00278     } else {
00279         m_cache->insertRequest(request, entries);
00280         emit signalEntriesLoaded(entries);
00281     }
00282     
00283     --m_numDataJobs;
00284     updateStatus();
00285 }
00286 
00287 void Engine::reloadEntries()
00288 {
00289     emit signalResetView();
00290     m_currentPage = -1;
00291     m_currentRequest.page = 0;
00292     m_numDataJobs = 0;
00293 
00294     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00295         if (p->isInitialized()) {
00296             if (m_currentRequest.sortMode == Provider::Installed) {
00297                 // when asking for installed entries, never use the cache
00298                 p->loadEntries(m_currentRequest);
00299             } else {
00300                 // take entries from cache until there are no more
00301                 EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
00302                 while (!cache.isEmpty()) {
00303                     kDebug() << "From cache";
00304                     emit signalEntriesLoaded(cache);
00305 
00306                     m_currentPage = m_currentRequest.page;
00307                     ++m_currentRequest.page;
00308                     cache = m_cache->requestFromCache(m_currentRequest);
00309                 }
00310                 // if the cache was empty, request data from provider
00311                 if (m_currentPage == -1) {
00312                     kDebug() << "From provider";
00313                     p->loadEntries(m_currentRequest);
00314 
00315                     ++m_numDataJobs;
00316                     updateStatus();
00317                 }
00318             }
00319         }
00320     }
00321 }
00322 
00323 void Engine::setCategoriesFilter(const QStringList& categories)
00324 {
00325     m_currentRequest.categories = categories;
00326     reloadEntries();
00327 }
00328 
00329 void Engine::setSortMode(Provider::SortMode mode)
00330 {
00331     if (m_currentRequest.sortMode != mode) {
00332         m_currentRequest.page = -1;
00333     }
00334     m_currentRequest.sortMode = mode;
00335     reloadEntries();
00336 }
00337 
00338 void Engine::setSearchTerm(const QString& searchString)
00339 {
00340     m_searchTimer->stop();
00341     m_currentRequest.searchTerm = searchString;
00342     EntryInternal::List cache = m_cache->requestFromCache(m_currentRequest);
00343     if (!cache.isEmpty()) {
00344         reloadEntries();
00345     } else {
00346         m_searchTimer->start();
00347     }
00348 }
00349 
00350 void Engine::slotSearchTimerExpired()
00351 {
00352     reloadEntries();
00353 }
00354 
00355 void Engine::requestMoreData()
00356 {
00357     kDebug() << "Get more data! current page: " << m_currentPage  << " requested: " << m_currentRequest.page;
00358 
00359     if (m_currentPage < m_currentRequest.page) {
00360         return;
00361     }
00362 
00363     m_currentRequest.page++;
00364     doRequest();
00365 }
00366 
00367 void Engine::requestData(int page, int pageSize)
00368 {
00369     m_currentRequest.page = page;
00370     m_currentRequest.pageSize = pageSize;
00371     doRequest();
00372 }
00373 
00374 void Engine::doRequest()
00375 {
00376     foreach (const QSharedPointer<KNS3::Provider> &p, m_providers) {
00377         if (p->isInitialized()) {
00378             p->loadEntries(m_currentRequest);
00379             ++m_numDataJobs;
00380             updateStatus();
00381         }
00382     }
00383 }
00384 
00385 void Engine::install(KNS3::EntryInternal entry, int linkId)
00386 {
00387     if (entry.status() == Entry::Updateable) {
00388         entry.setStatus(Entry::Updating);
00389     } else  {
00390         entry.setStatus(Entry::Installing);
00391     }
00392     emit signalEntryChanged(entry);
00393 
00394     kDebug() << "Install " << entry.name()
00395         << " from: " << entry.providerId();
00396     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00397     if (p) {
00398         p->loadPayloadLink(entry, linkId);
00399 
00400         ++m_numInstallJobs;
00401         updateStatus();
00402     }
00403 }
00404 
00405 void Engine::slotInstallationFinished()
00406 {
00407     --m_numInstallJobs;
00408     updateStatus();
00409 }
00410 
00411 void Engine::slotInstallationFailed(const QString& message)
00412 {
00413     --m_numInstallJobs;
00414     emit signalError(message);
00415 }
00416 
00417 void Engine::slotEntryDetailsLoaded(const KNS3::EntryInternal& entry)
00418 {
00419     emit signalEntryDetailsLoaded(entry);
00420 }
00421 
00422 void Engine::downloadLinkLoaded(const KNS3::EntryInternal& entry)
00423 {
00424     m_installation->install(entry);
00425 }
00426 
00427 void Engine::uninstall(KNS3::EntryInternal entry)
00428 {
00429     // FIXME: change the status?
00430     entry.setStatus(Entry::Installing);
00431     emit signalEntryChanged(entry);
00432     m_installation->uninstall(entry);
00433 }
00434 
00435 void Engine::loadDetails(const KNS3::EntryInternal &entry)
00436 {
00437     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00438     p->loadEntryDetails(entry);
00439 }
00440 
00441 void Engine::loadPreview(const KNS3::EntryInternal& entry, EntryInternal::PreviewType type)
00442 {
00443     kDebug() << "START  preview: " << entry.name() << type;
00444     ImageLoader* l = new ImageLoader(entry, type, this);
00445     connect(l, SIGNAL(signalPreviewLoaded(KNS3::EntryInternal,KNS3::EntryInternal::PreviewType)), this, SLOT(slotPreviewLoaded(KNS3::EntryInternal,KNS3::EntryInternal::PreviewType)));
00446     l->start();
00447     ++m_numPictureJobs;
00448     updateStatus();
00449 }
00450 
00451 void Engine::slotPreviewLoaded(const KNS3::EntryInternal& entry, EntryInternal::PreviewType type)
00452 {
00453     kDebug() << "FINISH preview: " << entry.name() << type;
00454     emit signalEntryPreviewLoaded(entry, type);
00455     --m_numPictureJobs;
00456     updateStatus();
00457 }
00458 
00459 void Engine::contactAuthor(const EntryInternal &entry)
00460 {
00461     if (!entry.author().email().isEmpty()) {
00462         // invoke mail with the address of the author
00463         KToolInvocation::invokeMailer(entry.author().email(), i18n("Re: %1", entry.name()));
00464     } else if (!entry.author().homepage().isEmpty()) {
00465         KToolInvocation::invokeBrowser(entry.author().homepage());
00466     }
00467 }
00468 
00469 void Engine::slotEntryChanged(const KNS3::EntryInternal& entry)
00470 {
00471     emit signalEntryChanged(entry);
00472 }
00473 
00474 bool Engine::userCanVote(const EntryInternal& entry)
00475 {
00476     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00477     return p->userCanVote();
00478 }
00479 
00480 void Engine::vote(const EntryInternal& entry, uint rating)
00481 {
00482     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00483     p->vote(entry, rating);
00484 }
00485 
00486 bool Engine::userCanBecomeFan(const EntryInternal& entry)
00487 {
00488     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00489     return p->userCanBecomeFan();
00490 }
00491 
00492 void Engine::becomeFan(const EntryInternal& entry)
00493 {
00494     QSharedPointer<Provider> p = m_providers.value(entry.providerId());
00495     p->becomeFan(entry);
00496 }
00497 
00498 void Engine::updateStatus()
00499 {
00500     if (m_numDataJobs > 0) {
00501         emit signalBusy(i18n("Loading data"));
00502     } else if (m_numPictureJobs > 0) {
00503         emit signalBusy(i18np("Loading one preview", "Loading %1 previews", m_numPictureJobs));
00504     } else if (m_numInstallJobs > 0) {
00505         emit signalBusy(i18n("Installing"));
00506     } else {
00507         emit signalIdle(QString());
00508     }
00509 }
00510 
00511 void Engine::checkForUpdates()
00512 {
00513     foreach(QSharedPointer<Provider> p, m_providers) {
00514         Provider::SearchRequest request(KNS3::Provider::Updates);
00515         p->loadEntries(request);
00516     }
00517 }
00518 
00519 #include "engine.moc"

KNewStuff

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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.5
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