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.
22 #include "applicationmodel.h"
25 #include <QtCore/QtAlgorithms>
26 #include <QtCore/QList>
27 #include <QtGui/QLabel>
28 #include <QtGui/QLayout>
29 #include <QtGui/QCheckBox>
32 #include <kauthorized.h>
33 #include <khistorycombobox.h>
34 #include <kdesktopfile.h>
35 #include <klineedit.h>
37 #include <kiconloader.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>
50 #include <kbuildsycocaprogressdialog.h>
51 #include <kconfiggroup.h>
54 #include "core/models.h"
56 using namespace Kickoff
;
59 void KConfigGroup::writeEntry( const char *pKey
,
60 const KGlobalSettings::Completion
& aValue
,
61 KConfigBase::WriteConfigFlags flags
)
63 writeEntry(pKey
, int(aValue
), flags
);
70 : isDir(false), parent(0), fetched(false)
88 QList
<AppNode
*> children
;
91 class ApplicationModelPrivate
94 ApplicationModelPrivate(ApplicationModel
*qq
)
97 duplicatePolicy(ApplicationModel::ShowDuplicatesPolicy
),
98 sortOrder(Qt::AscendingOrder
),
99 sortColumn(Qt::DisplayRole
)
103 ~ApplicationModelPrivate()
108 static bool AppNodeLessThan(AppNode
*n1
, AppNode
*n2
);
109 void fillNode(const QString
&relPath
, AppNode
*node
);
110 static QHash
<QString
,QString
> iconNameMap();
114 ApplicationModel::DuplicatePolicy duplicatePolicy
;
115 Qt::SortOrder sortOrder
;
119 bool ApplicationModelPrivate::AppNodeLessThan(AppNode
*n1
, AppNode
*n2
)
121 if (n1
->isDir
!= n2
->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
)
146 QString relPath
= _relPath
;
147 QString desktopEntry
;
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())
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
164 if (duplicatePolicy
== ApplicationModel::ShowLatestOnlyPolicy
&&
165 existingServices
.contains(appName
)
167 if (Kickoff::isLaterVersion(existingServices
[appName
],service
)) {
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)
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();
202 kWarning(250) << "KServiceGroup: Unexpected object in list!";
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()
231 bool ApplicationModel::canFetchMore(const QModelIndex
&parent
) const
233 if (!parent
.isValid())
236 AppNode
*node
= static_cast<AppNode
*>(parent
.internalPointer());
237 return node
->isDir
&& !node
->fetched
;
240 int ApplicationModel::columnCount(const QModelIndex
&parent
) const
246 QVariant
ApplicationModel::data(const QModelIndex
&index
, int role
) const
248 if (!index
.isValid())
251 AppNode
*node
= static_cast<AppNode
*>(index
.internalPointer());
254 case Qt::DisplayRole
:
255 if (!node
->genericName
.isEmpty()) {
256 return node
->genericName
;
258 return node
->appName
;
261 case Kickoff::SubTitleRole
:
262 if(!node
->genericName
.isEmpty()) {
263 return node
->appName
;
266 case Kickoff::UrlRole
:
267 return node
->desktopEntry
;
268 case Qt::DecorationRole
:
277 void ApplicationModel::fetchMore(const QModelIndex
&parent
)
279 if (!parent
.isValid())
282 AppNode
*node
= static_cast<AppNode
*>(parent
.internalPointer());
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())
297 AppNode
*node
= static_cast<AppNode
*>(parent
.internalPointer());
301 QVariant
ApplicationModel::headerData(int section
, Qt::Orientation orientation
, int role
) const
303 if (orientation
!= Qt::Horizontal
|| section
!= 0)
307 case Qt::DisplayRole
:
308 return i18n("Known Applications");
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();
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
);
342 return QModelIndex();
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
)
360 d
->duplicatePolicy
= policy
;
361 d
->root
= new AppNode();
362 d
->fillNode(QString(), d
->root
);
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
379 * This needs to be discussed on kde-core-devel and fixed
381 QHash
<QString
,QString
> ApplicationModelPrivate::iconNameMap()
383 static QHash
<QString
,QString
> map
;
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");
400 #include "applicationmodel.moc"