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

KNewStuff

  • sources
  • kde-4.12
  • kdelibs
  • knewstuff
  • knewstuff3
  • attica
atticaprovider.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 2009-2010 Frederik Gladhorn <gladhorn@kde.org>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Lesser General Public
6  License as published by the Free Software Foundation; either
7  version 2.1 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Lesser General Public License for more details.
13 
14  You should have received a copy of the GNU Lesser General Public
15  License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "atticaprovider.h"
19 
20 #include <kdebug.h>
21 #include <klocale.h>
22 #include <kio/job.h>
23 #include <kmessagebox.h>
24 
25 #include <attica/providermanager.h>
26 #include <attica/provider.h>
27 #include <attica/listjob.h>
28 #include <attica/content.h>
29 #include <attica/downloaditem.h>
30 #include <attica/accountbalance.h>
31 #include <attica/person.h>
32 
33 using namespace Attica;
34 
35 namespace KNS3
36 {
37 
38 AtticaProvider::AtticaProvider(const QStringList& categories)
39  : mEntryJob(0)
40  , mInitialized(false)
41 {
42  // init categories map with invalid categories
43  foreach (const QString& category, categories)
44  mCategoryMap.insert(category, Attica::Category());
45 
46  connect(&m_providerManager, SIGNAL(providerAdded(Attica::Provider)), SLOT(providerLoaded(Attica::Provider)));
47  connect(&m_providerManager, SIGNAL(authenticationCredentialsMissing(Provider)),
48  SLOT(authenticationCredentialsMissing(Provider)));
49 }
50 
51 AtticaProvider::AtticaProvider(const Attica::Provider& provider, const QStringList& categories)
52  : mEntryJob(0)
53  , mInitialized(false)
54 {
55  // init categories map with invalid categories
56  foreach (const QString& category, categories) {
57  mCategoryMap.insert(category, Attica::Category());
58  }
59  providerLoaded(provider);
60 }
61 
62 QString AtticaProvider::id() const
63 {
64  return m_provider.baseUrl().toString();
65 }
66 
67 void AtticaProvider::authenticationCredentialsMissing(const KNS3::Provider& )
68 {
69  kDebug() << "Authentication missing!";
70  // FIXME Show autentication dialog
71 }
72 
73 bool AtticaProvider::setProviderXML(const QDomElement & xmldata)
74 {
75  if (xmldata.tagName() != "provider")
76  return false;
77 
78  // FIXME this is quite ugly, repackaging the xml into a string
79  QDomDocument doc("temp");
80  kDebug(550) << "setting provider xml" << doc.toString();
81 
82  doc.appendChild(xmldata.cloneNode(true));
83  m_providerManager.addProviderFromXml(doc.toString());
84 
85  if (!m_providerManager.providers().isEmpty()) {
86  kDebug() << "base url of attica provider:" << m_providerManager.providers().last().baseUrl().toString();
87  } else {
88  kError() << "Could not load provider.";
89  return false;
90  }
91  return true;
92 }
93 
94 void AtticaProvider::setCachedEntries(const KNS3::EntryInternal::List& cachedEntries)
95 {
96  mCachedEntries = cachedEntries;
97 }
98 
99 void AtticaProvider::providerLoaded(const Attica::Provider& provider)
100 {
101  mName = provider.name();
102  kDebug() << "Added provider: " << provider.name();
103 
104  m_provider = provider;
105 
106  Attica::ListJob<Attica::Category>* job = m_provider.requestCategories();
107  connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(listOfCategoriesLoaded(Attica::BaseJob*)));
108  job->start();
109 }
110 
111 void AtticaProvider::listOfCategoriesLoaded(Attica::BaseJob* listJob)
112 {
113  if (!jobSuccess(listJob)) return;
114 
115  kDebug() << "loading categories: " << mCategoryMap.keys();
116 
117  Attica::ListJob<Attica::Category>* job = static_cast<Attica::ListJob<Attica::Category>*>(listJob);
118  Category::List categoryList = job->itemList();
119 
120  foreach(const Category& category, categoryList) {
121  if (mCategoryMap.contains(category.name())) {
122  kDebug() << "Adding category: " << category.name();
123  mCategoryMap[category.name()] = category;
124  }
125  }
126  mInitialized = true;
127  emit providerInitialized(this);
128 }
129 
130 bool AtticaProvider::isInitialized() const
131 {
132  return mInitialized;
133 }
134 
135 void AtticaProvider::loadEntries(const KNS3::Provider::SearchRequest& request)
136 {
137  if (mEntryJob) {
138  mEntryJob->abort();
139  mEntryJob = 0;
140  }
141 
142  mCurrentRequest = request;
143  if (request.sortMode == Installed) {
144  if (request.page == 0) {
145  emit loadingFinished(request, installedEntries());
146  } else {
147  emit loadingFinished(request, EntryInternal::List());
148  }
149  return;
150  }
151 
152  if (request.sortMode == Updates) {
153  checkForUpdates();
154  return;
155  }
156 
157  Attica::Provider::SortMode sorting = atticaSortMode(request.sortMode);
158  Attica::Category::List categoriesToSearch;
159 
160  if (request.categories.isEmpty()) {
161  // search in all categories
162  categoriesToSearch = mCategoryMap.values();
163  } else {
164  foreach (const QString& categoryName, request.categories) {
165  categoriesToSearch.append(mCategoryMap.value(categoryName));
166  }
167  }
168 
169  ListJob<Content>* job = m_provider.searchContents(categoriesToSearch, request.searchTerm, sorting, request.page, request.pageSize);
170  connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(categoryContentsLoaded(Attica::BaseJob*)));
171 
172  mEntryJob = job;
173  job->start();
174 }
175 
176 void AtticaProvider::checkForUpdates()
177 {
178  foreach (const EntryInternal& e, mCachedEntries) {
179  ItemJob<Content>* job = m_provider.requestContent(e.uniqueId());
180  connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(detailsLoaded(Attica::BaseJob*)));
181  m_updateJobs.insert(job);
182  job->start();
183  kDebug() << "Checking for update: " << e.name();
184  }
185 }
186 
187 void AtticaProvider::loadEntryDetails(const KNS3::EntryInternal& entry)
188 {
189  ItemJob<Content>* job = m_provider.requestContent(entry.uniqueId());
190  connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(detailsLoaded(Attica::BaseJob*)));
191  job->start();
192 }
193 
194 void AtticaProvider::detailsLoaded(BaseJob* job)
195 {
196  if (jobSuccess(job)) {
197  ItemJob<Content>* contentJob = static_cast<ItemJob<Content>*>(job);
198  Content content = contentJob->result();
199  EntryInternal entry = entryFromAtticaContent(content);
200  emit entryDetailsLoaded(entry);
201  kDebug() << "check update finished: " << entry.name();
202  }
203 
204  if (m_updateJobs.remove(job) && m_updateJobs.isEmpty()) {
205  kDebug() << "check update finished.";
206  QList<EntryInternal> updatable;
207  foreach(const EntryInternal& entry, mCachedEntries) {
208  if (entry.status() == Entry::Updateable) {
209  updatable.append(entry);
210  }
211  }
212  emit loadingFinished(mCurrentRequest, updatable);
213  }
214 }
215 
216 void AtticaProvider::categoryContentsLoaded(BaseJob* job)
217 {
218  if (!jobSuccess(job)) return;
219 
220  ListJob<Content>* listJob = static_cast<ListJob<Content>*>(job);
221  Content::List contents = listJob->itemList();
222 
223  EntryInternal::List entries;
224  Q_FOREACH(const Content &content, contents) {
225  mCachedContent.insert(content.id(), content);
226  entries.append(entryFromAtticaContent(content));
227  }
228 
229  kDebug() << "loaded: " << mCurrentRequest.hashForRequest() << " count: " << entries.size();
230  emit loadingFinished(mCurrentRequest, entries);
231  mEntryJob = 0;
232 }
233 
234 Attica::Provider::SortMode AtticaProvider::atticaSortMode(const SortMode& sortMode)
235 {
236  if (sortMode == Newest) {
237  return Attica::Provider::Newest;
238  }
239  if (sortMode == Alphabetical) {
240  return Attica::Provider::Alphabetical;
241  }
242  if (sortMode == Downloads) {
243  return Attica::Provider::Downloads;
244  }
245  return Attica::Provider::Rating;
246 }
247 
248 void AtticaProvider::loadPayloadLink(const KNS3::EntryInternal& entry, int linkId)
249 {
250  Attica::Content content = mCachedContent.value(entry.uniqueId());
251  DownloadDescription desc = content.downloadUrlDescription(linkId);
252 
253  if (desc.hasPrice()) {
254  // Ask for balance, then show information...
255  ItemJob<AccountBalance>* job = m_provider.requestAccountBalance();
256  connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(accountBalanceLoaded(Attica::BaseJob*)));
257  mDownloadLinkJobs[job] = qMakePair(entry, linkId);
258  job->start();
259 
260  kDebug() << "get account balance";
261  } else {
262  ItemJob<DownloadItem>* job = m_provider.downloadLink(entry.uniqueId(), QString::number(linkId));
263  connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(downloadItemLoaded(Attica::BaseJob*)));
264  mDownloadLinkJobs[job] = qMakePair(entry, linkId);
265  job->start();
266 
267  kDebug() << " link for " << entry.uniqueId();
268  }
269 }
270 
271 void AtticaProvider::accountBalanceLoaded(Attica::BaseJob* baseJob)
272 {
273  if (!jobSuccess(baseJob)) return;
274 
275  ItemJob<AccountBalance>* job = static_cast<ItemJob<AccountBalance>*>(baseJob);
276  AccountBalance item = job->result();
277 
278  QPair<EntryInternal, int> pair = mDownloadLinkJobs.take(job);
279  EntryInternal entry(pair.first);
280  Content content = mCachedContent.value(entry.uniqueId());
281  if (content.downloadUrlDescription(pair.second).priceAmount() < item.balance()) {
282  kDebug() << "Your balance is greather than the price."
283  << content.downloadUrlDescription(pair.second).priceAmount() << " balance: " << item.balance();
284  if (KMessageBox::questionYesNo(0,
285  i18nc("the price of a download item, parameter 1 is the currency, 2 is the price",
286  "This item costs %1 %2.\nDo you want to buy it?",
287  item.currency(), content.downloadUrlDescription(pair.second).priceAmount()
288  )) == KMessageBox::Yes) {
289  ItemJob<DownloadItem>* job = m_provider.downloadLink(entry.uniqueId(), QString::number(pair.second));
290  connect(job, SIGNAL(finished(Attica::BaseJob*)), SLOT(downloadItemLoaded(Attica::BaseJob*)));
291  connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*)));
292  mDownloadLinkJobs[job] = qMakePair(entry, pair.second);
293  job->start();
294  } else {
295  return;
296  }
297  } else {
298  kDebug() << "You don't have enough money on your account!"
299  << content.downloadUrlDescription(0).priceAmount() << " balance: " << item.balance();
300  KMessageBox::information(0, i18n("Your account balance is too low:\nYour balance: %1\nPrice: %2",
301  item.balance(),content.downloadUrlDescription(0).priceAmount()));
302  }
303 }
304 
305 void AtticaProvider::downloadItemLoaded(BaseJob* baseJob)
306 {
307  if (!jobSuccess(baseJob)) return;
308 
309  ItemJob<DownloadItem>* job = static_cast<ItemJob<DownloadItem>*>(baseJob);
310  DownloadItem item = job->result();
311 
312  EntryInternal entry = mDownloadLinkJobs.take(job).first;
313  entry.setPayload(QString(item.url().toString()));
314  emit payloadLinkLoaded(entry);
315 }
316 
317 EntryInternal::List AtticaProvider::installedEntries() const
318 {
319  EntryInternal::List entries;
320  foreach (const EntryInternal& entry, mCachedEntries) {
321  if (entry.status() == Entry::Installed || entry.status() == Entry::Updateable) {
322  entries.append(entry);
323  }
324  }
325  return entries;
326 }
327 
328 void AtticaProvider::vote(const EntryInternal& entry, uint rating)
329 {
330  PostJob * job = m_provider.voteForContent(entry.uniqueId(), rating);
331  connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(votingFinished(Attica::BaseJob*)));
332  connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*)));
333  job->start();
334 }
335 
336 void AtticaProvider::votingFinished(Attica::BaseJob* job)
337 {
338  if (!jobSuccess(job)) return;
339  emit signalInformation(i18nc("voting for an item (good/bad)", "Your vote was recorded."));
340 }
341 
342 void AtticaProvider::becomeFan(const EntryInternal& entry)
343 {
344  PostJob * job = m_provider.becomeFan(entry.uniqueId());
345  connect(job, SIGNAL(finished(Attica::BaseJob*)), this, SLOT(becomeFanFinished(Attica::BaseJob*)));
346  connect(job, SIGNAL(jobStarted(QNetworkReply*)), SLOT(atticaJobStarted(QNetworkReply*)));
347  job->start();
348 }
349 
350 void AtticaProvider::becomeFanFinished(Attica::BaseJob* job)
351 {
352  if (!jobSuccess(job)) return;
353  emit signalInformation(i18n("You are now a fan."));
354 }
355 
356 bool AtticaProvider::jobSuccess(Attica::BaseJob* job) const
357 {
358  if (job->metadata().error() == Attica::Metadata::NoError) {
359  return true;
360  }
361  kDebug() << "job error: " << job->metadata().error() << " status code: " << job->metadata().statusCode() << job->metadata().message();
362 
363  if (job->metadata().error() == Attica::Metadata::NetworkError) {
364  emit signalError(i18n("Network error. (%1)", job->metadata().statusCode()));
365  }
366  if (job->metadata().error() == Attica::Metadata::OcsError) {
367  if (job->metadata().statusCode() == 200) {
368  emit signalError(i18n("Too many requests to server. Please try again in a few minutes."));
369  } else {
370  emit signalError(i18n("Unknown Open Collaboration Service API error. (%1)", job->metadata().statusCode()));
371  }
372  }
373  return false;
374 }
375 
376 EntryInternal AtticaProvider::entryFromAtticaContent(const Attica::Content& content)
377 {
378  EntryInternal entry;
379 
380  entry.setProviderId(id());
381  entry.setUniqueId(content.id());
382  entry.setStatus(KNS3::Entry::Downloadable);
383  entry.setVersion(content.version());
384  entry.setReleaseDate(content.updated().date());
385 
386  int index = mCachedEntries.indexOf(entry);
387  if (index >= 0) {
388  EntryInternal &cacheEntry = mCachedEntries[index];
389  // check if updateable
390  if (((cacheEntry.status() == Entry::Installed) || (cacheEntry.status() == Entry::Updateable)) &&
391  ((cacheEntry.version() != entry.version()) || (cacheEntry.releaseDate() != entry.releaseDate()))) {
392  cacheEntry.setStatus(Entry::Updateable);
393  cacheEntry.setUpdateVersion(entry.version());
394  cacheEntry.setUpdateReleaseDate(entry.releaseDate());
395  }
396  entry = cacheEntry;
397  } else {
398  mCachedEntries.append(entry);
399  }
400 
401  entry.setName(content.name());
402  entry.setHomepage(content.detailpage());
403  entry.setRating(content.rating());
404  entry.setDownloadCount(content.downloads());
405  entry.setNumberFans(content.attribute("fans").toInt());
406  entry.setDonationLink(content.attribute("donationpage"));
407  entry.setKnowledgebaseLink(content.attribute("knowledgebasepage"));
408  entry.setNumberKnowledgebaseEntries(content.attribute("knowledgebaseentries").toInt());
409 
410  entry.setPreviewUrl(content.smallPreviewPicture("1"), EntryInternal::PreviewSmall1);
411  entry.setPreviewUrl(content.smallPreviewPicture("2"), EntryInternal::PreviewSmall2);
412  entry.setPreviewUrl(content.smallPreviewPicture("3"), EntryInternal::PreviewSmall3);
413 
414  entry.setPreviewUrl(content.previewPicture("1"), EntryInternal::PreviewBig1);
415  entry.setPreviewUrl(content.previewPicture("2"), EntryInternal::PreviewBig2);
416  entry.setPreviewUrl(content.previewPicture("3"), EntryInternal::PreviewBig3);
417 
418  entry.setLicense(content.license());
419  Author author;
420  author.setName(content.author());
421  author.setHomepage(content.attribute("profilepage"));
422  entry.setAuthor(author);
423 
424  entry.setSource(KNS3::EntryInternal::Online);
425  entry.setSummary(content.description());
426  entry.setChangelog(content.changelog());
427 
428  entry.clearDownloadLinkInformation();
429  QList<Attica::DownloadDescription> descs = content.downloadUrlDescriptions();
430  foreach (Attica::DownloadDescription desc, descs) {
431  EntryInternal::DownloadLinkInformation info;
432  info.name = desc.name();
433  info.priceAmount = desc.priceAmount();
434  info.distributionType = desc.distributionType();
435  info.descriptionLink = desc.link();
436  info.id = desc.id();
437  info.isDownloadtypeLink = desc.isDownloadtypLink();
438  entry.appendDownloadLinkInformation(info);
439  }
440 
441  return entry;
442 }
443 
444 } // namespace
445 
446 
447 #include "atticaprovider.moc"
KNS3::Provider
KNewStuff Base Provider class.
Definition: knewstuff3/core/provider.h:46
i18n
QString i18n(const char *text)
KNS3::EntryInternal::List
QList< EntryInternal > List
Definition: entryinternal.h:57
KNS3::EntryInternal::PreviewSmall1
Definition: entryinternal.h:71
kdebug.h
KNS3::EntryInternal
KNewStuff data entry container.
Definition: entryinternal.h:54
KNS3::EntryInternal::Online
Definition: entryinternal.h:66
KNS3::EntryInternal::name
QString name() const
Retrieve the name of the data object.
Definition: entryinternal.cpp:124
KNS3::Provider::Installed
Definition: knewstuff3/core/provider.h:57
KNS3::Provider::signalInformation
void signalInformation(const QString &) const
KNS3::Provider::Downloads
Definition: knewstuff3/core/provider.h:56
QNetworkReply
KMessageBox::information
static void information(QWidget *parent, const QString &text, const QString &caption=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
KNS3::AtticaProvider::id
virtual QString id() const
A unique Id for this provider (the url in most cases)
Definition: atticaprovider.cpp:62
kError
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
KNS3::AtticaProvider::isInitialized
virtual bool isInitialized() const
Definition: atticaprovider.cpp:130
KNS3::AtticaProvider::loadEntries
virtual void loadEntries(const KNS3::Provider::SearchRequest &request)
load the given search and return given page
Definition: atticaprovider.cpp:135
KNS3::AtticaProvider::setProviderXML
virtual bool setProviderXML(const QDomElement &xmldata)
set the provider data xml, to initialize the provider
Definition: atticaprovider.cpp:73
QString
KNS3::AtticaProvider::loadEntryDetails
virtual void loadEntryDetails(const KNS3::EntryInternal &entry)
Definition: atticaprovider.cpp:187
KNS3::EntryInternal::PreviewBig3
Definition: entryinternal.h:76
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
klocale.h
i18nc
QString i18nc(const char *ctxt, const char *text)
KNS3::Provider::payloadLinkLoaded
void payloadLinkLoaded(const KNS3::EntryInternal &)
KNS3::Provider::Updates
Definition: knewstuff3/core/provider.h:58
KNS3::Provider::SearchRequest
used to keep track of a search
Definition: knewstuff3/core/provider.h:64
KNS3::AtticaProvider::AtticaProvider
AtticaProvider(const QStringList &categories)
Definition: atticaprovider.cpp:38
KNS3::Provider::Newest
Definition: knewstuff3/core/provider.h:53
KNS3::Provider::Alphabetical
Definition: knewstuff3/core/provider.h:54
KNS3::Provider::providerInitialized
void providerInitialized(KNS3::Provider *)
atticaprovider.h
KNS3::Entry::Installed
Definition: knewstuff3/entry.h:61
QStringList
KNS3::EntryInternal::PreviewSmall2
Definition: entryinternal.h:72
KNS3::Provider::SearchRequest::searchTerm
QString searchTerm
Definition: knewstuff3/core/provider.h:66
KNS3::AtticaProvider::loadPayloadLink
virtual void loadPayloadLink(const EntryInternal &entry, int linkId)
Definition: atticaprovider.cpp:248
KNS3::Provider::SearchRequest::pageSize
int pageSize
Definition: knewstuff3/core/provider.h:69
KNS3::Entry::Downloadable
Definition: knewstuff3/entry.h:60
KNS3::EntryInternal::PreviewSmall3
Definition: entryinternal.h:73
KMessageBox::questionYesNo
static int questionYesNo(QWidget *parent, const QString &text, const QString &caption=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Notify)
KNS3::Provider::SearchRequest::page
int page
Definition: knewstuff3/core/provider.h:68
KNS3::Provider::loadingFinished
void loadingFinished(const KNS3::Provider::SearchRequest &, const KNS3::EntryInternal::List &) const
KNS3::EntryInternal::PreviewBig1
Definition: entryinternal.h:74
job.h
KNS3::AtticaProvider::setCachedEntries
virtual void setCachedEntries(const KNS3::EntryInternal::List &cachedEntries)
Definition: atticaprovider.cpp:94
KNS3::Provider::signalError
void signalError(const QString &) const
KNS3::Provider::SearchRequest::categories
QStringList categories
Definition: knewstuff3/core/provider.h:67
KNS3::Provider::mName
QString mName
Definition: knewstuff3/core/provider.h:148
QPair
KMessageBox::Yes
KNS3::AtticaProvider::vote
virtual void vote(const EntryInternal &entry, uint rating)
Definition: atticaprovider.cpp:328
KNS3::EntryInternal::uniqueId
QString uniqueId() const
Definition: entryinternal.cpp:134
KNS3::EntryInternal::PreviewBig2
Definition: entryinternal.h:75
KNS3::Provider::SearchRequest::hashForRequest
QString hashForRequest() const
Definition: knewstuff3/core/provider.cpp:33
kmessagebox.h
KNS3::Provider::entryDetailsLoaded
void entryDetailsLoaded(const KNS3::EntryInternal &)
KNS3::AtticaProvider::becomeFan
virtual void becomeFan(const EntryInternal &entry)
Definition: atticaprovider.cpp:342
KNS3::Entry::Updateable
Definition: knewstuff3/entry.h:62
QList< EntryInternal >
KNS3::Provider::SearchRequest::sortMode
SortMode sortMode
Definition: knewstuff3/core/provider.h:65
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:50:48 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

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 API Reference

Skip menu "kdelibs API Reference"
  • 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
  • kjsembed
  •   WTF
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Nepomuk-Core
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal