00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "applicationmodel.h"
00023
00024
00025 #include <QtCore/QtAlgorithms>
00026 #include <QtCore/QList>
00027 #include <QtGui/QLabel>
00028 #include <QtGui/QLayout>
00029 #include <QtGui/QCheckBox>
00030
00031
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
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 ,
00136 true ,
00137 false ,
00138 primaryNamePolicy == ApplicationModel::GenericNamePrimary );
00139
00140
00141 QHash<QString, KService::Ptr> existingServices;
00142
00143
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
00168
00169 if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
00170 existingServices.contains(appName)) {
00171 if (Kickoff::isLaterVersion(existingServices[appName], service)) {
00172 continue;
00173 } else {
00174
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
00186
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
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
00237
00238
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
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 }
00484
00485
00486 #include "applicationmodel.moc"