2 * This file is part of the KDE project
3 * Copyright (C) 2007 Rafael Fernández López <ereslibre@kde.org>
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 #include "dolphinmodel.h"
23 #include "dolphinsortfilterproxymodel.h"
25 #include "kcategorizedview.h"
27 #include <config-nepomuk.h>
29 #include <nepomuk/global.h>
30 #include <nepomuk/resource.h>
31 #include <nepomuk/tag.h>
32 #include <Soprano/Vocabulary/Xesam>
35 #include <kdatetime.h>
36 #include <kdirmodel.h>
37 #include <kfileitem.h>
38 #include <kiconloader.h>
42 #include <kmimetype.h>
43 #include <kstandarddirs.h>
46 #include <QSortFilterProxyModel>
48 #include <QPersistentModelIndex>
52 const char* DolphinModel::m_others
= I18N_NOOP2("@title:group Name", "Others");
54 DolphinModel::DolphinModel(QObject
* parent
) :
56 m_hasRevisionData(false),
61 DolphinModel::~DolphinModel()
65 bool DolphinModel::setData(const QModelIndex
& index
, const QVariant
& value
, int role
)
67 if ((index
.column() == DolphinModel::Revision
) && (role
== Qt::DecorationRole
)) {
68 // TODO: remove data again when items are deleted...
70 const QPersistentModelIndex key
= index
;
71 const RevisionControlPlugin::RevisionState state
= static_cast<RevisionControlPlugin::RevisionState
>(value
.toInt());
72 if (m_revisionHash
.value(key
, RevisionControlPlugin::UnversionedRevision
) != state
) {
73 if (!m_hasRevisionData
) {
74 connect(this, SIGNAL(rowsRemoved (const QModelIndex
&, int, int)),
75 this, SLOT(slotRowsRemoved(const QModelIndex
&, int, int)));
76 m_hasRevisionData
= true;
79 m_revisionHash
.insert(key
, state
);
80 emit
dataChanged(index
, index
);
85 return KDirModel::setData(index
, value
, role
);
88 QVariant
DolphinModel::data(const QModelIndex
& index
, int role
) const
91 case KCategorizedSortFilterProxyModel::CategoryDisplayRole
:
92 return displayRoleData(index
);
94 case KCategorizedSortFilterProxyModel::CategorySortRole
:
95 return sortRoleData(index
);
97 case Qt::DecorationRole
:
98 if (index
.column() == DolphinModel::Revision
) {
99 return m_revisionHash
.value(index
, RevisionControlPlugin::UnversionedRevision
);
103 case Qt::DisplayRole
:
104 if (index
.column() == DolphinModel::Revision
) {
105 switch (m_revisionHash
.value(index
, RevisionControlPlugin::UnversionedRevision
)) {
106 case RevisionControlPlugin::NormalRevision
:
107 return i18nc("@item::intable", "Normal");
108 case RevisionControlPlugin::UpdateRequiredRevision
:
109 return i18nc("@item::intable", "Update required");
110 case RevisionControlPlugin::LocallyModifiedRevision
:
111 return i18nc("@item::intable", "Locally modified");
112 case RevisionControlPlugin::AddedRevision
:
113 return i18nc("@item::intable", "Added");
114 case RevisionControlPlugin::RemovedRevision
:
115 return i18nc("@item::intable", "Removed");
116 case RevisionControlPlugin::ConflictingRevision
:
117 return i18nc("@item::intable", "Conflicting");
118 case RevisionControlPlugin::UnversionedRevision
:
120 return i18nc("@item::intable", "Unversioned");
129 return KDirModel::data(index
, role
);
132 QVariant
DolphinModel::headerData(int section
, Qt::Orientation orientation
, int role
) const
134 if ((orientation
== Qt::Horizontal
) && (role
== Qt::DisplayRole
)) {
135 if (section
< KDirModel::ColumnCount
) {
136 return KDirModel::headerData(section
, orientation
, role
);
139 Q_ASSERT(section
== DolphinModel::Revision
);
140 return i18nc("@title::column", "Revision");
145 int DolphinModel::columnCount(const QModelIndex
& parent
) const
147 return KDirModel::columnCount(parent
) + (ExtraColumnCount
- ColumnCount
);
150 void DolphinModel::clearRevisionData()
152 m_revisionHash
.clear();
153 m_hasRevisionData
= false;
156 bool DolphinModel::hasRevisionData() const
158 return m_hasRevisionData
;
161 void DolphinModel::slotRowsRemoved(const QModelIndex
& parent
, int start
, int end
)
163 if (m_hasRevisionData
) {
164 const int column
= parent
.column();
165 for (int row
= start
; row
<= end
; ++row
) {
166 m_revisionHash
.remove(parent
.child(row
, column
));
171 QVariant
DolphinModel::displayRoleData(const QModelIndex
& index
) const
175 if (!index
.isValid()) {
179 const KDirModel
*dirModel
= qobject_cast
<const KDirModel
*>(index
.model());
180 KFileItem item
= dirModel
->itemForIndex(index
);
182 switch (index
.column()) {
183 case KDirModel::Name
: {
184 // KDirModel checks columns to know to which role are
186 const QModelIndex nameIndex
= index
.model()->index(index
.row(), KDirModel::Name
, index
.parent());
187 if (!nameIndex
.isValid()) {
190 const QVariant data
= nameIndex
.model()->data(nameIndex
, Qt::DisplayRole
);
191 const QString name
= data
.toString();
192 if (!name
.isEmpty()) {
193 if (!item
.isHidden() && name
.at(0).isLetter())
194 retString
= name
.at(0).toUpper();
195 else if (item
.isHidden()) {
196 if (name
.at(0) == '.') {
197 if (name
.size() > 1 && name
.at(1).isLetter()) {
198 retString
= name
.at(1).toUpper();
200 retString
= i18nc("@title:group Name", m_others
);
203 retString
= name
.at(0).toUpper();
206 bool validCategory
= false;
208 const QString
str(name
.toUpper());
209 const QChar
* currA
= str
.unicode();
210 while (!currA
->isNull() && !validCategory
) {
211 if (currA
->isLetter()) {
212 validCategory
= true;
213 } else if (currA
->isDigit()) {
214 return i18nc("@title:group Name", m_others
);
220 if (!validCategory
) {
221 retString
= validCategory
? *currA
: i18nc("@title:group Name", m_others
);
230 case KDirModel::Size
: {
231 const KIO::filesize_t fileSize
= !item
.isNull() ? item
.size() : ~0U;
232 if (!item
.isNull() && item
.isDir()) {
233 retString
= i18nc("@title:group Size", "Folders");
234 } else if (fileSize
< 5242880) {
235 retString
= i18nc("@title:group Size", "Small");
236 } else if (fileSize
< 10485760) {
237 retString
= i18nc("@title:group Size", "Medium");
239 retString
= i18nc("@title:group Size", "Big");
244 case KDirModel::ModifiedTime
: {
245 KDateTime modifiedTime
= item
.time(KFileItem::ModificationTime
);
246 modifiedTime
= modifiedTime
.toLocalZone();
248 const QDate currentDate
= KDateTime::currentLocalDateTime().date();
249 const QDate modifiedDate
= modifiedTime
.date();
251 const int daysDistance
= modifiedDate
.daysTo(currentDate
);
253 int yearForCurrentWeek
= 0;
254 int currentWeek
= currentDate
.weekNumber(&yearForCurrentWeek
);
255 if (yearForCurrentWeek
== currentDate
.year() + 1) {
259 int yearForModifiedWeek
= 0;
260 int modifiedWeek
= modifiedDate
.weekNumber(&yearForModifiedWeek
);
261 if (yearForModifiedWeek
== modifiedDate
.year() + 1) {
265 if (currentDate
.year() == modifiedDate
.year() && currentDate
.month() == modifiedDate
.month()) {
266 switch (currentWeek
- modifiedWeek
) {
268 switch (daysDistance
) {
269 case 0: retString
= i18nc("@title:group Date", "Today"); break;
270 case 1: retString
= i18nc("@title:group Date", "Yesterday"); break;
271 default: retString
= modifiedTime
.toString(i18nc("@title:group The week day name: %A", "%A"));
275 retString
= i18nc("@title:group Date", "Last Week");
278 retString
= i18nc("@title:group Date", "Two Weeks Ago");
281 retString
= i18nc("@title:group Date", "Three Weeks Ago");
285 retString
= i18nc("@title:group Date", "Earlier this Month");
291 const QDate lastMonthDate
= currentDate
.addMonths(-1);
292 if (lastMonthDate
.year() == modifiedDate
.year() && lastMonthDate
.month() == modifiedDate
.month()) {
293 if (daysDistance
== 1) {
294 retString
= modifiedTime
.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Yesterday (%B, %Y)"));
295 } else if (daysDistance
<= 7) {
296 retString
= modifiedTime
.toString(i18nc("@title:group The week day name: %A, %B is full month name in current locale, and %Y is full year number", "%A (%B, %Y)"));
297 } else if (daysDistance
<= 7 * 2) {
298 retString
= modifiedTime
.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Last Week (%B, %Y)"));
299 } else if (daysDistance
<= 7 * 3) {
300 retString
= modifiedTime
.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Two Weeks Ago (%B, %Y)"));
301 } else if (daysDistance
<= 7 * 4) {
302 retString
= modifiedTime
.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Three Weeks Ago (%B, %Y)"));
304 retString
= modifiedTime
.toString(i18nc("@title:group Date: %B is full month name in current locale, and %Y is full year number", "Earlier on %B, %Y"));
307 retString
= modifiedTime
.toString(i18nc("@title:group The month and year: %B is full month name in current locale, and %Y is full year number", "%B, %Y"));
313 case KDirModel::Permissions
: {
318 QFileInfo
info(item
.url().pathOrUrl());
321 if (info
.permission(QFile::ReadUser
)) {
322 user
= i18nc("@item:intext Access permission, concatenated", "Read, ");
324 if (info
.permission(QFile::WriteUser
)) {
325 user
+= i18nc("@item:intext Access permission, concatenated", "Write, ");
327 if (info
.permission(QFile::ExeUser
)) {
328 user
+= i18nc("@item:intext Access permission, concatenated", "Execute, ");
330 user
= user
.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : user
.mid(0, user
.count() - 2);
333 if (info
.permission(QFile::ReadGroup
)) {
334 group
= i18nc("@item:intext Access permission, concatenated", "Read, ");
336 if (info
.permission(QFile::WriteGroup
)) {
337 group
+= i18nc("@item:intext Access permission, concatenated", "Write, ");
339 if (info
.permission(QFile::ExeGroup
)) {
340 group
+= i18nc("@item:intext Access permission, concatenated", "Execute, ");
342 group
= group
.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : group
.mid(0, group
.count() - 2);
344 // set permission string
345 if (info
.permission(QFile::ReadOther
)) {
346 others
= i18nc("@item:intext Access permission, concatenated", "Read, ");
348 if (info
.permission(QFile::WriteOther
)) {
349 others
+= i18nc("@item:intext Access permission, concatenated", "Write, ");
351 if (info
.permission(QFile::ExeOther
)) {
352 others
+= i18nc("@item:intext Access permission, concatenated", "Execute, ");
354 others
= others
.isEmpty() ? i18nc("@item:intext Access permission, concatenated", "Forbidden") : others
.mid(0, others
.count() - 2);
356 retString
= i18nc("@title:group Files and folders by permissions", "(User: %1) (Group: %2) (Others: %3)", user
, group
, others
);
360 case KDirModel::Owner
:
361 retString
= item
.user();
364 case KDirModel::Group
:
365 retString
= item
.group();
368 case KDirModel::Type
:
369 retString
= item
.mimeComment();
372 case DolphinModel::Revision
:
380 QVariant
DolphinModel::sortRoleData(const QModelIndex
& index
) const
384 if (!index
.isValid()) {
388 const KDirModel
*dirModel
= qobject_cast
<const KDirModel
*>(index
.model());
389 KFileItem item
= dirModel
->itemForIndex(index
);
391 switch (index
.column()) {
392 case KDirModel::Name
: {
393 retVariant
= data(index
, KCategorizedSortFilterProxyModel::CategoryDisplayRole
);
394 if (retVariant
== i18nc("@title:group Name", m_others
)) {
395 // assure that the "Others" group is always the last categorization
396 retVariant
= QString(QChar(QChar::ReplacementCharacter
));
401 case KDirModel::Size
: {
402 const KIO::filesize_t fileSize
= !item
.isNull() ? item
.size() : ~0U;
405 } else if (fileSize
< 5242880) {
407 } else if (fileSize
< 10485760) {
415 case KDirModel::ModifiedTime
: {
416 KDateTime modifiedTime
= item
.time(KFileItem::ModificationTime
);
417 modifiedTime
= modifiedTime
.toLocalZone();
419 const QDate currentDate
= KDateTime::currentLocalDateTime().date();
420 const QDate modifiedDate
= modifiedTime
.date();
422 retVariant
= -modifiedDate
.daysTo(currentDate
);
426 case KDirModel::Permissions
: {
427 QFileInfo
info(item
.url().pathOrUrl());
429 retVariant
= -KDirSortFilterProxyModel::pointsForPermissions(info
);
433 case KDirModel::Owner
:
434 retVariant
= item
.user();
437 case KDirModel::Group
:
438 retVariant
= item
.group();
441 case KDirModel::Type
:
443 // when sorting we want folders to be placed first
444 retVariant
= QString(); // krazy:exclude=nullstrassign
446 retVariant
= item
.mimeComment();