Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / plasma / applets / kickoff / core / applicationmodel.cpp
blob999971c15a96f3f9bb31e6c19f7e77d2908de4fd
1 /*
2 Copyright 2007 Pino Toscano <pino@kde.org>
3 Copyright 2007 Robert Knight <robertknight@gmail.com>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 // Own
22 #include "applicationmodel.h"
24 // Qt
25 #include <QtCore/QtAlgorithms>
26 #include <QtCore/QList>
27 #include <QtGui/QLabel>
28 #include <QtGui/QLayout>
29 #include <QtGui/QCheckBox>
31 // KDE
32 #include <kauthorized.h>
33 #include <khistorycombobox.h>
34 #include <kdesktopfile.h>
35 #include <klineedit.h>
36 #include <klocale.h>
37 #include <kiconloader.h>
38 #include <krun.h>
39 #include <kstandarddirs.h>
40 #include <kstringhandler.h>
41 #include <kmimetypetrader.h>
42 #include <kurlcompletion.h>
43 #include <kurlrequester.h>
44 #include <kmimetype.h>
45 #include <kservicegroup.h>
46 #include <kdebug.h>
48 #include <assert.h>
49 #include <stdlib.h>
50 #include <kbuildsycocaprogressdialog.h>
51 #include <kconfiggroup.h>
53 // Local
54 #include "core/models.h"
56 using namespace Kickoff;
58 template <> inline
59 void KConfigGroup::writeEntry( const char *pKey,
60 const KGlobalSettings::Completion& aValue,
61 KConfigBase::WriteConfigFlags flags)
63 writeEntry(pKey, int(aValue), flags);
66 class AppNode
68 public:
69 AppNode()
70 : isDir(false), parent(0), fetched(false)
73 ~AppNode()
75 qDeleteAll(children);
78 QIcon icon;
79 QString genericName;
80 QString appName;
81 QString relPath;
82 QString desktopEntry;
83 bool isDir;
85 AppNode *parent;
86 bool fetched;
88 QList<AppNode*> children;
91 class ApplicationModelPrivate
93 public:
94 ApplicationModelPrivate(ApplicationModel *qq)
95 : q(qq),
96 root(new AppNode()),
97 duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy),
98 sortOrder(Qt::AscendingOrder),
99 sortColumn(Qt::DisplayRole)
103 ~ApplicationModelPrivate()
105 delete root;
108 static bool AppNodeLessThan(AppNode *n1, AppNode *n2);
109 void fillNode(const QString &relPath, AppNode *node);
110 static QHash<QString,QString> iconNameMap();
112 ApplicationModel *q;
113 AppNode *root;
114 ApplicationModel::DuplicatePolicy duplicatePolicy;
115 Qt::SortOrder sortOrder;
116 int sortColumn;
119 bool ApplicationModelPrivate::AppNodeLessThan(AppNode *n1, AppNode *n2)
121 if (n1->isDir != n2->isDir) {
122 return n1->isDir;
125 const QString s1 = n1->genericName.isEmpty() ? n1->appName : n1->genericName;
126 const QString s2 = n2->genericName.isEmpty() ? n2->appName : n2->genericName;
128 return s1.compare(s2, Qt::CaseInsensitive) < 0;
131 void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
133 KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
134 if (!root || !root->isValid()) return;
136 KServiceGroup::List list = root->entries();
138 // application name <-> service map for detecting duplicate entries
139 QHash<QString,KService::Ptr> existingServices;
140 for (KServiceGroup::List::ConstIterator it = list.begin();
141 it != list.end(); ++it)
143 QString icon;
144 QString appName;
145 QString genericName;
146 QString relPath = _relPath;
147 QString desktopEntry;
148 bool isDir = false;
149 const KSycocaEntry::Ptr p = (*it);
150 if (p->isType(KST_KService))
152 const KService::Ptr service = KService::Ptr::staticCast(p);
154 if (service->noDisplay())
155 continue;
157 icon = service->icon();
158 appName = service->name();
159 genericName = service->genericName();
160 desktopEntry = service->entryPath();
162 // check for duplicates (eg. KDE 3 and KDE 4 versions of application
163 // both present)
164 if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
165 existingServices.contains(appName)
167 if (Kickoff::isLaterVersion(existingServices[appName],service)) {
168 continue;
169 } else {
170 // find and remove the existing entry with the same name
171 for (int i = 0 ; i < node->children.count() ; i++) {
172 if ( node->children[i]->appName == appName ) {
173 delete node->children.takeAt(i);
178 existingServices[appName] = service;
180 else if (p->isType(KST_KServiceGroup))
182 const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
184 if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0)
185 continue;
187 kDebug(250) << "Service group" << serviceGroup->entryPath() << serviceGroup->icon()
188 << serviceGroup->relPath() << serviceGroup->directoryEntryPath();
190 icon = serviceGroup->icon();
191 if (iconNameMap().contains(icon)) {
192 icon = iconNameMap().value(icon);
195 genericName = serviceGroup->caption();
196 relPath = serviceGroup->relPath();
197 appName = serviceGroup->comment();
198 isDir = true;
200 else
202 kWarning(250) << "KServiceGroup: Unexpected object in list!";
203 continue;
206 AppNode *newnode = new AppNode();
207 newnode->icon = KIcon(icon);
208 newnode->appName = appName;
209 newnode->genericName = genericName;
210 newnode->relPath = relPath;
211 newnode->desktopEntry = desktopEntry;
212 newnode->isDir = isDir;
213 newnode->parent = node;
214 node->children.append(newnode);
217 qStableSort(node->children.begin(), node->children.end(), ApplicationModelPrivate::AppNodeLessThan);
220 ApplicationModel::ApplicationModel(QObject *parent)
221 : KickoffAbstractModel(parent), d(new ApplicationModelPrivate(this))
223 d->fillNode(QString(), d->root);
226 ApplicationModel::~ApplicationModel()
228 delete d;
231 bool ApplicationModel::canFetchMore(const QModelIndex &parent) const
233 if (!parent.isValid())
234 return false;
236 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
237 return node->isDir && !node->fetched;
240 int ApplicationModel::columnCount(const QModelIndex &parent) const
242 Q_UNUSED(parent)
243 return 1;
246 QVariant ApplicationModel::data(const QModelIndex &index, int role) const
248 if (!index.isValid())
249 return QVariant();
251 AppNode *node = static_cast<AppNode*>(index.internalPointer());
253 switch (role) {
254 case Qt::DisplayRole:
255 if (!node->genericName.isEmpty()) {
256 return node->genericName;
257 } else {
258 return node->appName;
260 break;
261 case Kickoff::SubTitleRole:
262 if(!node->genericName.isEmpty()) {
263 return node->appName;
265 break;
266 case Kickoff::UrlRole:
267 return node->desktopEntry;
268 case Qt::DecorationRole:
269 return node->icon;
270 break;
271 default:
274 return QVariant();
277 void ApplicationModel::fetchMore(const QModelIndex &parent)
279 if (!parent.isValid())
280 return;
282 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
283 if (!node->isDir)
284 return;
286 emit layoutAboutToBeChanged();
287 d->fillNode(node->relPath, node);
288 node->fetched = true;
289 emit layoutChanged();
292 bool ApplicationModel::hasChildren(const QModelIndex &parent) const
294 if (!parent.isValid())
295 return true;
297 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
298 return node->isDir;
301 QVariant ApplicationModel::headerData(int section, Qt::Orientation orientation, int role) const
303 if (orientation != Qt::Horizontal || section != 0)
304 return QVariant();
306 switch (role) {
307 case Qt::DisplayRole:
308 return i18n("Known Applications");
309 break;
310 default:
311 return QVariant();
315 QModelIndex ApplicationModel::index(int row, int column, const QModelIndex &parent) const
317 if (row < 0 || column != 0)
318 return QModelIndex();
320 AppNode *node = d->root;
321 if (parent.isValid())
322 node = static_cast<AppNode*>(parent.internalPointer());
324 if (row >= node->children.count())
325 return QModelIndex();
326 else
327 return createIndex(row, 0, node->children.at(row));
330 QModelIndex ApplicationModel::parent(const QModelIndex &index) const
332 if (!index.isValid())
333 return QModelIndex();
335 AppNode *node = static_cast<AppNode*>(index.internalPointer());
336 if (node->parent->parent) {
337 int id = node->parent->parent->children.indexOf(node->parent);
339 if (id >= 0 && id < node->parent->parent->children.count())
340 return createIndex(id, 0, node->parent);
341 else
342 return QModelIndex();
344 else
345 return QModelIndex();
348 int ApplicationModel::rowCount(const QModelIndex &parent) const
350 if (!parent.isValid())
351 return d->root->children.count();
353 AppNode *node = static_cast<AppNode*>(parent.internalPointer());
354 return node->children.count();
357 void ApplicationModel::setDuplicatePolicy(DuplicatePolicy policy)
359 delete d->root;
360 d->duplicatePolicy = policy;
361 d->root = new AppNode();
362 d->fillNode(QString(), d->root);
363 reset();
365 ApplicationModel::DuplicatePolicy ApplicationModel::duplicatePolicy() const
367 return d->duplicatePolicy;
371 * FIXME This is a temporary workaround to map the icon names found
372 * in the desktop directory files (from /usr/share/desktop-directories)
373 * into the Oxygen icon names. (Only applies if the Gnome menu files
374 * are also installed)
376 * This list was compiled from Kubuntu 7.04 with the gnome-menus
377 * package present.
379 * This needs to be discussed on kde-core-devel and fixed
381 QHash<QString,QString> ApplicationModelPrivate::iconNameMap()
383 static QHash<QString,QString> map;
384 if (map.isEmpty()) {
385 map.insert("gnome-util","applications-accessories");
386 // accessibility Oxygen icon was missing when this list was compiled
387 map.insert("accessibility-directory","applications-other");
388 map.insert("gnome-devel","applications-development");
389 map.insert("package_edutainment","applications-education");
390 map.insert("gnome-joystick","applications-games");
391 map.insert("gnome-graphics","applications-graphics");
392 map.insert("gnome-globe","applications-internet");
393 map.insert("gnome-multimedia","applications-multimedia");
394 map.insert("gnome-applications","applications-office");
395 map.insert("gnome-system","applications-system");
397 return map;
400 #include "applicationmodel.moc"