• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Applets

applicationmodel.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2007 Pino Toscano <pino@kde.org>
00003     Copyright 2007 Robert Knight <robertknight@gmail.com>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 // Own
00022 #include "applicationmodel.h"
00023 
00024 // Qt
00025 #include <QtCore/QtAlgorithms>
00026 #include <QtCore/QList>
00027 #include <QtGui/QLabel>
00028 #include <QtGui/QLayout>
00029 #include <QtGui/QCheckBox>
00030 
00031 // KDE
00032 #include <kauthorized.h>
00033 #include <khistorycombobox.h>
00034 #include <kdesktopfile.h>
00035 #include <klineedit.h>
00036 #include <klocale.h>
00037 #include <kiconloader.h>
00038 #include <krun.h>
00039 #include <kstandarddirs.h>
00040 #include <kstringhandler.h>
00041 #include <kmimetypetrader.h>
00042 #include <kurlcompletion.h>
00043 #include <kurlrequester.h>
00044 #include <kmimetype.h>
00045 #include <kservicegroup.h>
00046 #include <ksycoca.h>
00047 #include <kdebug.h>
00048 
00049 #include <assert.h>
00050 #include <stdlib.h>
00051 #include <kbuildsycocaprogressdialog.h>
00052 #include <kconfiggroup.h>
00053 #include "kickoffadaptor.h"
00054 // Local
00055 #include "core/models.h"
00056 
00057 template <> inline
00058 void KConfigGroup::writeEntry(const char *pKey,
00059                               const KGlobalSettings::Completion& aValue,
00060                               KConfigBase::WriteConfigFlags flags)
00061 {
00062     writeEntry(pKey, int(aValue), flags);
00063 }
00064 
00065 namespace Kickoff
00066 {
00067 
00068 class AppNode
00069 {
00070 public:
00071     AppNode()
00072         : parent(0),
00073           fetched(false),
00074           isDir(false),
00075           subTitleMandatory(false)
00076     {
00077     }
00078 
00079     ~AppNode()
00080     {
00081         qDeleteAll(children);
00082     }
00083 
00084     QList<AppNode*> children;
00085 
00086     QIcon icon;
00087     QString genericName;
00088     QString appName;
00089     QString relPath;
00090     QString desktopEntry;
00091 
00092     AppNode *parent;
00093     bool fetched;
00094     bool isDir;
00095     bool subTitleMandatory;
00096 };
00097 
00098 class ApplicationModelPrivate
00099 {
00100 public:
00101     ApplicationModelPrivate(ApplicationModel *qq)
00102             : q(qq),
00103               root(new AppNode()),
00104               duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy),
00105               systemApplicationPolicy(ApplicationModel::ShowSystemOnlyPolicy),
00106               primaryNamePolicy(ApplicationModel::GenericNamePrimary)
00107     {
00108         systemApplications = Kickoff::systemApplicationList();
00109     }
00110 
00111     ~ApplicationModelPrivate()
00112     {
00113         delete root;
00114     }
00115 
00116     void fillNode(const QString &relPath, AppNode *node);
00117     static QHash<QString, QString> iconNameMap();
00118 
00119     ApplicationModel *q;
00120     AppNode *root;
00121     ApplicationModel::DuplicatePolicy duplicatePolicy;
00122     ApplicationModel::SystemApplicationPolicy systemApplicationPolicy;
00123     ApplicationModel::PrimaryNamePolicy primaryNamePolicy;
00124     QStringList systemApplications;
00125 };
00126 
00127 void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
00128 {
00129     KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
00130 
00131     if (!root || !root->isValid()) {
00132         return;
00133     }
00134 
00135     const KServiceGroup::List list = root->entries(true /* sorted */,
00136                                                    true /* exclude no display entries */,
00137                                                    false /* allow separators */,
00138                                                    primaryNamePolicy == ApplicationModel::GenericNamePrimary /* sort by generic name */);
00139 
00140     // application name <-> service map for detecting duplicate entries
00141     QHash<QString, KService::Ptr> existingServices;
00142 
00143     // generic name <-> node mapping to determinate duplicate generic names
00144     QHash<QString,QList<AppNode*> > genericNames;
00145 
00146     for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
00147         QString icon;
00148         QString appName;
00149         QString genericName;
00150         QString relPath = _relPath;
00151         QString desktopEntry;
00152         bool isDir = false;
00153         const KSycocaEntry::Ptr p = (*it);
00154 
00155         if (p->isType(KST_KService)) {
00156             const KService::Ptr service = KService::Ptr::staticCast(p);
00157 
00158             if (service->noDisplay()) {
00159                 continue;
00160             }
00161 
00162             icon = service->icon();
00163             appName = service->name();
00164             genericName = service->genericName();
00165             desktopEntry = service->entryPath();
00166 
00167             // check for duplicates (eg. KDE 3 and KDE 4 versions of application
00168             // both present)
00169             if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
00170                     existingServices.contains(appName)) {
00171                 if (Kickoff::isLaterVersion(existingServices[appName], service)) {
00172                     continue;
00173                 } else {
00174                     // find and remove the existing entry with the same name
00175                     for (int i = 0 ; i < node->children.count() ; i++) {
00176                         if (node->children[i]->appName == appName) {
00177                             delete node->children.takeAt(i);
00178                         }
00179                     }
00180                 }
00181             }
00182 
00183             if (systemApplicationPolicy == ApplicationModel::ShowSystemOnlyPolicy &&
00184                     systemApplications.contains(service->desktopEntryName())) {
00185                 // don't duplicate applications that are configured to appear in the System tab
00186                 // in the Applications tab
00187                 continue;
00188             }
00189 
00190             existingServices[appName] = service;
00191         } else if (p->isType(KST_KServiceGroup)) {
00192             const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
00193 
00194             if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0)
00195                 continue;
00196 
00197             kDebug(250) << "Service group" << serviceGroup->entryPath() << serviceGroup->icon()
00198             << serviceGroup->relPath() << serviceGroup->directoryEntryPath();
00199 
00200             icon = serviceGroup->icon();
00201             if (iconNameMap().contains(icon)) {
00202                 icon = iconNameMap().value(icon);
00203             }
00204 
00205             genericName = serviceGroup->caption();
00206             relPath = serviceGroup->relPath();
00207             appName = serviceGroup->comment();
00208             isDir = true;
00209         } else if (p->isType(KST_KServiceSeparator)) {
00210             // TODO: implement seaparators
00211         } else {
00212             kWarning(250) << "KServiceGroup: Unexpected object in list!";
00213             continue;
00214         }
00215 
00216         AppNode *newnode = new AppNode();
00217         newnode->icon = KIcon(icon);
00218         newnode->appName = appName;
00219         newnode->genericName = genericName;
00220         newnode->relPath = relPath;
00221         newnode->desktopEntry = desktopEntry;
00222         newnode->isDir = isDir;
00223         newnode->parent = node;
00224         node->children.append(newnode);
00225 
00226         if (p->isType(KST_KService)) {
00227             const QString s = genericName.toLower();
00228             if (genericNames.contains(s)) {
00229                 genericNames[s].append(newnode);
00230             } else {
00231                 genericNames[s] = QList<AppNode*>() << newnode;
00232             }
00233         }
00234     }
00235 
00236     // set the subTitleMandatory field for nodes that do not provide a unique generic
00237     // name what may help us on display to show in such cases also the subtitle to
00238     // provide a hint to the user what the duplicate entries are about.
00239     foreach (QList<AppNode*> list, genericNames.values()) {
00240         if (list.count() >= 2) {
00241             foreach (AppNode* n, list) {
00242                 n->subTitleMandatory = true;
00243             }
00244         }
00245     }
00246 }
00247 
00248 ApplicationModel::ApplicationModel(QObject *parent)
00249         : KickoffAbstractModel(parent), d(new ApplicationModelPrivate(this))
00250 {
00251     QDBusConnection dbus = QDBusConnection::sessionBus();
00252     (void)new KickoffAdaptor(this);
00253     QDBusConnection::sessionBus().registerObject("/kickoff", this);
00254     dbus.connect(QString(), "/kickoff", "org.kde.plasma", "reloadMenu", this, SLOT(reloadMenu()));
00255     connect(KSycoca::self(), SIGNAL(databaseChanged()), this, SLOT(checkSycocaChange()));
00256     d->fillNode(QString(), d->root);
00257 }
00258 
00259 ApplicationModel::~ApplicationModel()
00260 {
00261     delete d;
00262 }
00263 
00264 bool ApplicationModel::canFetchMore(const QModelIndex &parent) const
00265 {
00266     if (!parent.isValid())
00267         return false;
00268 
00269     AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00270     return node->isDir && !node->fetched;
00271 }
00272 
00273 int ApplicationModel::columnCount(const QModelIndex &parent) const
00274 {
00275     Q_UNUSED(parent)
00276     return 1;
00277 }
00278 
00279 QVariant ApplicationModel::data(const QModelIndex &index, int role) const
00280 {
00281     if (!index.isValid())
00282         return QVariant();
00283 
00284     AppNode *node = static_cast<AppNode*>(index.internalPointer());
00285 
00286     switch (role) {
00287     case Qt::DisplayRole:
00288         if (!node->genericName.isEmpty()) {
00289             return node->genericName;
00290         } else {
00291             return node->appName;
00292         }
00293         break;
00294     case Kickoff::SubTitleRole:
00295         if (!node->genericName.isEmpty()) {
00296             return node->appName;
00297         }
00298         break;
00299     case Kickoff::UrlRole:
00300         return node->desktopEntry;
00301         break;
00302     case Kickoff::SubTitleMandatoryRole:
00303         return node->subTitleMandatory;
00304         break;
00305     case Qt::DecorationRole:
00306         return node->icon;
00307         break;
00308     default:
00309         ;
00310     }
00311     return QVariant();
00312 }
00313 
00314 void ApplicationModel::fetchMore(const QModelIndex &parent)
00315 {
00316     if (!parent.isValid()) {
00317         return;
00318     }
00319 
00320     AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00321     if (!node->isDir) {
00322         return;
00323     }
00324 
00325     emit layoutAboutToBeChanged();
00326     d->fillNode(node->relPath, node);
00327     node->fetched = true;
00328     emit layoutChanged();
00329 }
00330 
00331 bool ApplicationModel::hasChildren(const QModelIndex &parent) const
00332 {
00333     if (!parent.isValid()) {
00334         return true;
00335     }
00336 
00337     AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00338     return node->isDir;
00339 }
00340 
00341 QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
00342 {
00343     if (orientation != Qt::Horizontal || section != 0) {
00344         return QVariant();
00345     }
00346 
00347     switch (role) {
00348     case Qt::DisplayRole:
00349         return i18n("Known Applications");
00350         break;
00351     default:
00352         return QVariant();
00353     }
00354 }
00355 
00356 QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const
00357 {
00358     if (row < 0 || column != 0)
00359         return QModelIndex();
00360 
00361     AppNode *node = d->root;
00362     if (parent.isValid())
00363         node = static_cast<AppNode*>(parent.internalPointer());
00364 
00365     if (row >= node->children.count())
00366         return QModelIndex();
00367     else
00368         return createIndex(row, 0, node->children.at(row));
00369 }
00370 
00371 QModelIndex ApplicationModel::parent(const QModelIndex &index) const
00372 {
00373     if (!index.isValid()) {
00374         return QModelIndex();
00375     }
00376 
00377     AppNode *node = static_cast<AppNode*>(index.internalPointer());
00378     if (node->parent->parent) {
00379         int id = node->parent->parent->children.indexOf(node->parent);
00380 
00381         if (id >= 0 && id < node->parent->parent->children.count()) {
00382             return createIndex(id, 0, node->parent);
00383         }
00384     }
00385 
00386     return QModelIndex();
00387 }
00388 
00389 int ApplicationModel::rowCount(const QModelIndex &parent) const
00390 {
00391     if (!parent.isValid()) {
00392         return d->root->children.count();
00393     }
00394 
00395     AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00396     return node->children.count();
00397 }
00398 
00399 void ApplicationModel::setDuplicatePolicy(DuplicatePolicy policy)
00400 {
00401     if (d->duplicatePolicy != policy) {
00402         d->duplicatePolicy = policy;
00403         reloadMenu();
00404     }
00405 }
00406 
00407 void ApplicationModel::setSystemApplicationPolicy(SystemApplicationPolicy policy)
00408 {
00409     if (d->systemApplicationPolicy != policy) {
00410         d->systemApplicationPolicy = policy;
00411         reloadMenu();
00412     }
00413 }
00414 
00415 void ApplicationModel::setPrimaryNamePolicy(PrimaryNamePolicy policy)
00416 {
00417     if (policy != d->primaryNamePolicy) {
00418         d->primaryNamePolicy = policy;
00419         reloadMenu();
00420     }
00421 }
00422 
00423 ApplicationModel::PrimaryNamePolicy ApplicationModel::primaryNamePolicy() const
00424 {
00425     return d->primaryNamePolicy;
00426 }
00427 
00428 void ApplicationModel::reloadMenu()
00429 {
00430     delete d->root;
00431     d->root = new AppNode();
00432     d->fillNode(QString(), d->root);
00433     reset();
00434 }
00435 
00436 void ApplicationModel::checkSycocaChange()
00437 {
00438     if (KSycoca::self()->isChanged("services")) {
00439         reloadMenu();
00440     }
00441 }
00442 
00443 ApplicationModel::DuplicatePolicy ApplicationModel::duplicatePolicy() const
00444 {
00445     return d->duplicatePolicy;
00446 }
00447 
00448 ApplicationModel::SystemApplicationPolicy ApplicationModel::systemApplicationPolicy() const
00449 {
00450     return d->systemApplicationPolicy;
00451 }
00452 
00464 QHash<QString, QString> ApplicationModelPrivate::iconNameMap()
00465 {
00466     static QHash<QString, QString> map;
00467     if (map.isEmpty()) {
00468         map.insert("gnome-util", "applications-accessories");
00469         // accessibility Oxygen icon was missing when this list was compiled
00470         map.insert("accessibility-directory", "applications-other");
00471         map.insert("gnome-devel", "applications-development");
00472         map.insert("package_edutainment", "applications-education");
00473         map.insert("gnome-joystick", "applications-games");
00474         map.insert("gnome-graphics", "applications-graphics");
00475         map.insert("gnome-globe", "applications-internet");
00476         map.insert("gnome-multimedia", "applications-multimedia");
00477         map.insert("gnome-applications", "applications-office");
00478         map.insert("gnome-system", "applications-system");
00479     }
00480     return map;
00481 }
00482 
00483 } // namespace Kickoff
00484 
00485 
00486 #include "applicationmodel.moc"

Applets

Skip menu "Applets"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
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