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 : isDir(false), parent(0), fetched(false)
00073 {
00074 }
00075 ~AppNode()
00076 {
00077 qDeleteAll(children);
00078 }
00079
00080 QIcon icon;
00081 QString genericName;
00082 QString appName;
00083 QString relPath;
00084 QString desktopEntry;
00085 bool isDir;
00086
00087 AppNode *parent;
00088 bool fetched;
00089
00090 QList<AppNode*> children;
00091 };
00092
00093 class ApplicationModelPrivate
00094 {
00095 public:
00096 ApplicationModelPrivate(ApplicationModel *qq)
00097 : q(qq),
00098 root(new AppNode()),
00099 duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy),
00100 sortOrder(Qt::AscendingOrder),
00101 sortColumn(Qt::DisplayRole)
00102 {
00103 systemApplications = Kickoff::systemApplicationList();
00104 }
00105
00106 ~ApplicationModelPrivate()
00107 {
00108 delete root;
00109 }
00110
00111 static bool AppNodeLessThan(AppNode *n1, AppNode *n2);
00112 void fillNode(const QString &relPath, AppNode *node);
00113 static QHash<QString,QString> iconNameMap();
00114
00115 ApplicationModel *q;
00116 AppNode *root;
00117 ApplicationModel::DuplicatePolicy duplicatePolicy;
00118 Qt::SortOrder sortOrder;
00119 int sortColumn;
00120 QStringList systemApplications;
00121 };
00122
00123 bool ApplicationModelPrivate::AppNodeLessThan(AppNode *n1, AppNode *n2)
00124 {
00125 if (n1->isDir != n2->isDir) {
00126 return n1->isDir;
00127 }
00128
00129 const QString s1 = n1->genericName.isEmpty() ? n1->appName : n1->genericName;
00130 const QString s2 = n2->genericName.isEmpty() ? n2->appName : n2->genericName;
00131
00132 return s1.compare(s2, Qt::CaseInsensitive) < 0;
00133 }
00134
00135 void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
00136 {
00137 KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
00138 if (!root || !root->isValid()) return;
00139
00140 KServiceGroup::List list = root->entries();
00141
00142
00143 QHash<QString,KService::Ptr> existingServices;
00144 for (KServiceGroup::List::ConstIterator it = list.begin();
00145 it != list.end(); ++it)
00146 {
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 if (p->isType(KST_KService))
00155 {
00156 const KService::Ptr service = KService::Ptr::staticCast(p);
00157
00158 if (service->noDisplay())
00159 continue;
00160
00161 icon = service->icon();
00162 appName = service->name();
00163 genericName = service->genericName();
00164 desktopEntry = service->entryPath();
00165
00166
00167
00168 if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
00169 existingServices.contains(appName)
00170 ) {
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
00184 if ( systemApplications.contains( service->desktopEntryName() ) ) {
00185 continue;
00186 }
00187
00188 existingServices[appName] = service;
00189 }
00190 else if (p->isType(KST_KServiceGroup))
00191 {
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 }
00210 else
00211 {
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
00227 qStableSort(node->children.begin(), node->children.end(), ApplicationModelPrivate::AppNodeLessThan);
00228 }
00229
00230 ApplicationModel::ApplicationModel(QObject *parent)
00231 : KickoffAbstractModel(parent), d(new ApplicationModelPrivate(this))
00232 {
00233 QDBusConnection dbus = QDBusConnection::sessionBus();
00234 (void)new KickoffAdaptor(this);
00235 QDBusConnection::sessionBus().registerObject("/kickoff", this);
00236 dbus.connect(QString(), "/kickoff", "org.kde.plasma", "reloadMenu", this, SLOT(slotReloadMenu()));
00237 connect(KSycoca::self(), SIGNAL(databaseChanged()), this, SLOT(checkSycocaChange()));
00238 d->fillNode(QString(), d->root);
00239 }
00240
00241 ApplicationModel::~ApplicationModel()
00242 {
00243 delete d;
00244 }
00245
00246 bool ApplicationModel::canFetchMore(const QModelIndex &parent) const
00247 {
00248 if (!parent.isValid())
00249 return false;
00250
00251 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00252 return node->isDir && !node->fetched;
00253 }
00254
00255 int ApplicationModel::columnCount(const QModelIndex &parent) const
00256 {
00257 Q_UNUSED(parent)
00258 return 1;
00259 }
00260
00261 QVariant ApplicationModel::data(const QModelIndex &index, int role) const
00262 {
00263 if (!index.isValid())
00264 return QVariant();
00265
00266 AppNode *node = static_cast<AppNode*>(index.internalPointer());
00267
00268 switch (role) {
00269 case Qt::DisplayRole:
00270 if (!node->genericName.isEmpty()) {
00271 return node->genericName;
00272 } else {
00273 return node->appName;
00274 }
00275 break;
00276 case Kickoff::SubTitleRole:
00277 if(!node->genericName.isEmpty()) {
00278 return node->appName;
00279 }
00280 break;
00281 case Kickoff::UrlRole:
00282 return node->desktopEntry;
00283 case Qt::DecorationRole:
00284 return node->icon;
00285 break;
00286 default:
00287 ;
00288 }
00289 return QVariant();
00290 }
00291
00292 void ApplicationModel::fetchMore(const QModelIndex &parent)
00293 {
00294 if (!parent.isValid())
00295 return;
00296
00297 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00298 if (!node->isDir)
00299 return;
00300
00301 emit layoutAboutToBeChanged();
00302 d->fillNode(node->relPath, node);
00303 node->fetched = true;
00304 emit layoutChanged();
00305 }
00306
00307 bool ApplicationModel::hasChildren(const QModelIndex &parent) const
00308 {
00309 if (!parent.isValid())
00310 return true;
00311
00312 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00313 return node->isDir;
00314 }
00315
00316 QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
00317 {
00318 if (orientation != Qt::Horizontal || section != 0)
00319 return QVariant();
00320
00321 switch (role) {
00322 case Qt::DisplayRole:
00323 return i18n("Known Applications");
00324 break;
00325 default:
00326 return QVariant();
00327 }
00328 }
00329
00330 QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const
00331 {
00332 if (row < 0 || column != 0)
00333 return QModelIndex();
00334
00335 AppNode *node = d->root;
00336 if (parent.isValid())
00337 node = static_cast<AppNode*>(parent.internalPointer());
00338
00339 if (row >= node->children.count())
00340 return QModelIndex();
00341 else
00342 return createIndex(row, 0, node->children.at(row));
00343 }
00344
00345 QModelIndex ApplicationModel::parent(const QModelIndex &index) const
00346 {
00347 if (!index.isValid())
00348 return QModelIndex();
00349
00350 AppNode *node = static_cast<AppNode*>(index.internalPointer());
00351 if (node->parent->parent) {
00352 int id = node->parent->parent->children.indexOf(node->parent);
00353
00354 if (id >= 0 && id < node->parent->parent->children.count())
00355 return createIndex(id, 0, node->parent);
00356 else
00357 return QModelIndex();
00358 }
00359 else
00360 return QModelIndex();
00361 }
00362
00363 int ApplicationModel::rowCount(const QModelIndex &parent) const
00364 {
00365 if (!parent.isValid())
00366 return d->root->children.count();
00367
00368 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
00369 return node->children.count();
00370 }
00371
00372 void ApplicationModel::setDuplicatePolicy(DuplicatePolicy policy)
00373 {
00374 delete d->root;
00375 d->duplicatePolicy = policy;
00376 d->root = new AppNode();
00377 d->fillNode(QString(), d->root);
00378 reset();
00379 }
00380
00381 void ApplicationModel::slotReloadMenu()
00382 {
00383 delete d->root;
00384 d->root = new AppNode();
00385 d->fillNode(QString(), d->root);
00386 reset();
00387 }
00388
00389 void ApplicationModel::checkSycocaChange()
00390 {
00391 if (KSycoca::self()->isChanged("services")) {
00392 slotReloadMenu();
00393 }
00394 }
00395
00396 ApplicationModel::DuplicatePolicy ApplicationModel::duplicatePolicy() const
00397 {
00398 return d->duplicatePolicy;
00399 }
00400
00412 QHash<QString,QString> ApplicationModelPrivate::iconNameMap()
00413 {
00414 static QHash<QString,QString> map;
00415 if (map.isEmpty()) {
00416 map.insert("gnome-util","applications-accessories");
00417
00418 map.insert("accessibility-directory","applications-other");
00419 map.insert("gnome-devel","applications-development");
00420 map.insert("package_edutainment","applications-education");
00421 map.insert("gnome-joystick","applications-games");
00422 map.insert("gnome-graphics","applications-graphics");
00423 map.insert("gnome-globe","applications-internet");
00424 map.insert("gnome-multimedia","applications-multimedia");
00425 map.insert("gnome-applications","applications-office");
00426 map.insert("gnome-system","applications-system");
00427 }
00428 return map;
00429 }
00430
00431 }
00432
00433
00434 #include "applicationmodel.moc"