diff options
| author | Peter Penz <[email protected]> | 2012-04-01 16:42:46 +0200 |
|---|---|---|
| committer | Peter Penz <[email protected]> | 2012-04-01 16:46:54 +0200 |
| commit | a4ede4e0ebaf0bba57da8fc2924577f1a8ff7b54 (patch) | |
| tree | 954d5394838230f20646f17727460aaae1fbcb34 /src/kitemviews | |
| parent | aced85db1f7c72268860f0368402cf97f34e55c2 (diff) | |
Allow showing Nepomuk metadata inside views
Metadata like image-size, rating, comments, tags, ... can be shown
now in the view (e.g. as column in the Details mode).
Still open: The rating-information needs to be shown as stars.
In the context of this feature also the following bugs have been
fixed:
- Fix visual glitches in the header of the Details mode
- Improve the minimum column width calculation to respect also
the headling and not only the content
BUG: 296782
FIXED-IN: 4.9.0
Diffstat (limited to 'src/kitemviews')
| -rw-r--r-- | src/kitemviews/kfileitemlistwidget.cpp | 123 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemlistwidget.h | 21 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemmodel.cpp | 70 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemmodel.h | 10 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemmodelrolesupdater.cpp | 36 | ||||
| -rw-r--r-- | src/kitemviews/kfileitemmodelrolesupdater.h | 8 | ||||
| -rw-r--r-- | src/kitemviews/kitemlistheaderwidget.cpp | 30 | ||||
| -rw-r--r-- | src/kitemviews/kitemlistheaderwidget_p.h | 4 | ||||
| -rw-r--r-- | src/kitemviews/kitemlistview.cpp | 40 | ||||
| -rw-r--r-- | src/kitemviews/knepomukrolesprovider.cpp | 153 | ||||
| -rw-r--r-- | src/kitemviews/knepomukrolesprovider_p.h | 66 |
11 files changed, 429 insertions, 132 deletions
diff --git a/src/kitemviews/kfileitemlistwidget.cpp b/src/kitemviews/kfileitemlistwidget.cpp index 022f3b472..4511b3b37 100644 --- a/src/kitemviews/kfileitemlistwidget.cpp +++ b/src/kitemviews/kfileitemlistwidget.cpp @@ -55,8 +55,7 @@ KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : m_scaledPixmapSize(), m_iconRect(), m_hoverPixmap(), - m_textPos(), - m_text(), + m_textInfo(), m_textRect(), m_sortedVisibleRoles(), m_expansionArea(), @@ -64,14 +63,12 @@ KFileItemListWidget::KFileItemListWidget(QGraphicsItem* parent) : m_additionalInfoTextColor(), m_overlay() { - for (int i = 0; i < TextIdCount; ++i) { - m_text[i].setTextFormat(Qt::PlainText); - m_text[i].setPerformanceHint(QStaticText::AggressiveCaching); - } } KFileItemListWidget::~KFileItemListWidget() { + qDeleteAll(m_textInfo); + m_textInfo.clear(); } void KFileItemListWidget::setLayout(Layout layout) @@ -131,7 +128,8 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte painter->setFont(itemListStyleOption.font); painter->setPen(textColor()); - painter->drawStaticText(m_textPos[Name], m_text[Name]); + const TextInfo* textInfo = m_textInfo.value("name"); + painter->drawStaticText(textInfo->pos, textInfo->staticText); bool clipAdditionalInfoBounds = false; if (m_supportsItemExpanding) { @@ -139,7 +137,7 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte // with the icon. This can happen if the user has minimized the width // of the name-column to a very small value. const qreal minX = m_pixmapPos.x() + m_pixmap.width() + 4 * itemListStyleOption.padding; - if (m_textPos[Name].x() + columnWidth("name") > minX) { + if (textInfo->pos.x() + columnWidth("name") > minX) { clipAdditionalInfoBounds = true; painter->save(); painter->setClipRect(minX, 0, size().width() - minX, size().height(), Qt::IntersectClip); @@ -148,8 +146,10 @@ void KFileItemListWidget::paint(QPainter* painter, const QStyleOptionGraphicsIte painter->setPen(m_additionalInfoTextColor); painter->setFont(itemListStyleOption.font); - for (int i = Name + 1; i < TextIdCount; ++i) { - painter->drawStaticText(m_textPos[i], m_text[i]); + + for (int i = 1; i < m_sortedVisibleRoles.count(); ++i) { + const TextInfo* textInfo = m_textInfo.value(m_sortedVisibleRoles[i]); + painter->drawStaticText(textInfo->pos, textInfo->staticText); } if (clipAdditionalInfoBounds) { @@ -579,7 +579,8 @@ void KFileItemListWidget::updatePixmapCache() int scaledIconSize = 0; if (iconOnTop) { - scaledIconSize = static_cast<int>(m_textPos[Name].y() - 2 * padding); + const TextInfo* textInfo = m_textInfo.value("name"); + scaledIconSize = static_cast<int>(textInfo->pos.y() - 2 * padding); } else { const int textRowsCount = (m_layout == CompactLayout) ? visibleRoles().count() : 1; const qreal requiredTextHeight = textRowsCount * option.fontMetrics.height(); @@ -599,7 +600,8 @@ void KFileItemListWidget::updatePixmapCache() m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height()); } else { // Center horizontally and vertically within the icon-area - m_pixmapPos.setX(m_textPos[Name].x() - 2 * padding + const TextInfo* textInfo = m_textInfo.value("name"); + m_pixmapPos.setX(textInfo->pos.x() - 2 * padding - (scaledIconSize + m_scaledPixmapSize.width()) / 2); m_pixmapPos.setY(padding + (scaledIconSize - m_scaledPixmapSize.height()) / 2); @@ -641,9 +643,14 @@ void KFileItemListWidget::updateTextsCache() break; } - for (int i = 0; i < TextIdCount; ++i) { - m_text[i].setText(QString()); - m_text[i].setTextOption(textOption); + qDeleteAll(m_textInfo); + m_textInfo.clear(); + for (int i = 0; i < m_sortedVisibleRoles.count(); ++i) { + TextInfo* textInfo = new TextInfo(); + textInfo->staticText.setTextFormat(Qt::PlainText); + textInfo->staticText.setPerformanceHint(QStaticText::AggressiveCaching); + textInfo->staticText.setTextOption(textOption); + m_textInfo.insert(m_sortedVisibleRoles[i], textInfo); } switch (m_layout) { @@ -676,15 +683,16 @@ void KFileItemListWidget::updateIconsLayoutTextCache() // Initialize properties for the "name" role. It will be used as anchor // for initializing the position of the other roles. - m_text[Name].setText(KStringHandler::preProcessWrap(values["name"].toString())); + TextInfo* nameTextInfo = m_textInfo.value("name"); + nameTextInfo->staticText.setText(KStringHandler::preProcessWrap(values["name"].toString())); // Calculate the number of lines required for the name and the required width int textLinesCountForName = 0; qreal requiredWidthForName = 0; QTextLine line; - QTextLayout layout(m_text[Name].text(), option.font); - layout.setTextOption(m_text[Name].textOption()); + QTextLayout layout(nameTextInfo->staticText.text(), option.font); + layout.setTextOption(nameTextInfo->staticText.textOption()); layout.beginLayout(); while ((line = layout.createLine()).isValid()) { line.setLineWidth(maxWidth); @@ -698,28 +706,28 @@ void KFileItemListWidget::updateIconsLayoutTextCache() const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0); textLinesCount += additionalRolesCount; - m_text[Name].setTextWidth(maxWidth); - m_textPos[Name] = QPointF(padding, widgetHeight - textLinesCount * fontHeight - padding); + nameTextInfo->staticText.setTextWidth(maxWidth); + nameTextInfo->pos = QPointF(padding, widgetHeight - textLinesCount * fontHeight - padding); m_textRect = QRectF(padding + (maxWidth - requiredWidthForName) / 2, - m_textPos[Name].y(), + nameTextInfo->pos.y(), requiredWidthForName, textLinesCountForName * fontHeight); // Calculate the position for each additional information - qreal y = m_textPos[Name].y() + textLinesCountForName * fontHeight; + qreal y = nameTextInfo->pos.y() + textLinesCountForName * fontHeight; foreach (const QByteArray& role, m_sortedVisibleRoles) { - const TextId textId = roleTextId(role); - if (textId == Name) { + if (role == "name") { continue; } const QString text = roleText(role, values); - m_text[textId].setText(text); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); qreal requiredWidth = 0; QTextLayout layout(text, option.font); - layout.setTextOption(m_text[textId].textOption()); + layout.setTextOption(textInfo->staticText.textOption()); layout.beginLayout(); QTextLine textLine = layout.createLine(); if (textLine.isValid()) { @@ -731,13 +739,13 @@ void KFileItemListWidget::updateIconsLayoutTextCache() // not get elided although it does not fit into the given width. As workaround // the padding is substracted. const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth - padding); - m_text[textId].setText(elidedText); + textInfo->staticText.setText(elidedText); } } layout.endLayout(); - m_textPos[textId] = QPointF(padding, y); - m_text[textId].setTextWidth(maxWidth); + textInfo->pos = QPointF(padding, y); + textInfo->staticText.setTextWidth(maxWidth); const QRectF textRect(padding + (maxWidth - requiredWidth) / 2, y, requiredWidth, fontHeight); m_textRect |= textRect; @@ -768,20 +776,19 @@ void KFileItemListWidget::updateCompactLayoutTextCache() qreal y = (widgetHeight - textLinesHeight) / 2; const qreal maxWidth = size().width() - x - option.padding; foreach (const QByteArray& role, m_sortedVisibleRoles) { - const TextId textId = roleTextId(role); - const QString text = roleText(role, values); - m_text[textId].setText(text); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); qreal requiredWidth = option.fontMetrics.width(text); if (requiredWidth > maxWidth) { requiredWidth = maxWidth; const QString elidedText = option.fontMetrics.elidedText(text, Qt::ElideRight, maxWidth); - m_text[textId].setText(elidedText); + textInfo->staticText.setText(elidedText); } - m_textPos[textId] = QPointF(x, y); - m_text[textId].setTextWidth(maxWidth); + textInfo->pos = QPointF(x, y); + textInfo->staticText.setTextWidth(maxWidth); maximumRequiredTextWidth = qMax(maximumRequiredTextWidth, requiredWidth); @@ -820,7 +827,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() const qreal y = qMax(qreal(option.padding), (widgetHeight - fontHeight) / 2); foreach (const QByteArray& role, m_sortedVisibleRoles) { - const TextId textId = roleTextId(role); + const RoleType type = roleType(role); QString text = roleText(role, values); @@ -828,7 +835,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() qreal requiredWidth = option.fontMetrics.width(text); const qreal roleWidth = columnWidth(role); qreal availableTextWidth = roleWidth - 2 * columnPadding; - if (textId == Name) { + if (type == Name) { availableTextWidth -= firstColumnInc; } @@ -837,16 +844,17 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() requiredWidth = option.fontMetrics.width(text); } - m_text[textId].setText(text); - m_textPos[textId] = QPointF(x + columnPadding, y); + TextInfo* textInfo = m_textInfo.value(role); + textInfo->staticText.setText(text); + textInfo->pos = QPointF(x + columnPadding, y); x += roleWidth; - switch (textId) { + switch (type) { case Name: { const qreal textWidth = option.extendedSelectionRegion - ? size().width() - m_textPos[textId].x() + ? size().width() - textInfo->pos.x() : requiredWidth + 2 * option.padding; - m_textRect = QRectF(m_textPos[textId].x() - option.padding, 0, + m_textRect = QRectF(textInfo->pos.x() - option.padding, 0, textWidth, size().height()); // The column after the name should always be aligned on the same x-position independent @@ -856,7 +864,7 @@ void KFileItemListWidget::updateDetailsLayoutTextCache() } case Size: // The values for the size should be right aligned - m_textPos[textId].rx() += roleWidth - requiredWidth - 2 * columnPadding; + textInfo->pos.rx() += roleWidth - requiredWidth - 2 * columnPadding; break; default: @@ -976,22 +984,16 @@ void KFileItemListWidget::applyHiddenEffect(QPixmap& pixmap) KIconEffect::semiTransparent(pixmap); } -KFileItemListWidget::TextId KFileItemListWidget::roleTextId(const QByteArray& role) +KFileItemListWidget::RoleType KFileItemListWidget::roleType(const QByteArray& role) { - static QHash<QByteArray, TextId> rolesHash; + static QHash<QByteArray, RoleType> rolesHash; if (rolesHash.isEmpty()) { rolesHash.insert("name", Name); rolesHash.insert("size", Size); rolesHash.insert("date", Date); - rolesHash.insert("permissions", Permissions); - rolesHash.insert("owner", Owner); - rolesHash.insert("group", Group); - rolesHash.insert("type", Type); - rolesHash.insert("destination", Destination); - rolesHash.insert("path", Path); } - return rolesHash.value(role); + return rolesHash.value(role, Generic); } QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteArray, QVariant>& values) @@ -999,17 +1001,7 @@ QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteA QString text; const QVariant roleValue = values.value(role); - switch (roleTextId(role)) { - case Name: - case Permissions: - case Owner: - case Group: - case Type: - case Destination: - case Path: - text = roleValue.toString(); - break; - + switch (roleType(role)) { case Size: { if (values.value("isDir").toBool()) { // The item represents a directory. Show the number of sub directories @@ -1038,6 +1030,11 @@ QString KFileItemListWidget::roleText(const QByteArray& role, const QHash<QByteA break; } + case Name: + case Generic: + text = roleValue.toString(); + break; + default: Q_ASSERT(false); break; diff --git a/src/kitemviews/kfileitemlistwidget.h b/src/kitemviews/kfileitemlistwidget.h index 76d090040..d68a22405 100644 --- a/src/kitemviews/kfileitemlistwidget.h +++ b/src/kitemviews/kfileitemlistwidget.h @@ -109,17 +109,11 @@ private slots: void slotCutItemsChanged(); private: - enum TextId { + enum RoleType { Name, Size, Date, - Permissions, - Owner, - Group, - Type, - Destination, - Path, - TextIdCount // Mandatory last entry + Generic // Mandatory last entry }; void triggerCacheRefreshing(); @@ -139,7 +133,7 @@ private: static QPixmap pixmapForIcon(const QString& name, int size); static void applyCutEffect(QPixmap& pixmap); static void applyHiddenEffect(QPixmap& pixmap); - static TextId roleTextId(const QByteArray& role); + static RoleType roleType(const QByteArray& role); /** * @return Shown string for the role \p role of the item with the values \p values. @@ -164,8 +158,13 @@ private: QRectF m_iconRect; // Cache for KItemListWidget::iconRect() QPixmap m_hoverPixmap; // Cache for modified m_pixmap when hovering the item - QPointF m_textPos[TextIdCount]; - QStaticText m_text[TextIdCount]; + struct TextInfo + { + QPointF pos; + QStaticText staticText; + }; + QHash<QByteArray, TextInfo*> m_textInfo; + QRectF m_textRect; QList<QByteArray> m_sortedVisibleRoles; diff --git a/src/kitemviews/kfileitemmodel.cpp b/src/kitemviews/kfileitemmodel.cpp index efc7b326e..1d7366cb2 100644 --- a/src/kitemviews/kfileitemmodel.cpp +++ b/src/kitemviews/kfileitemmodel.cpp @@ -274,19 +274,8 @@ QList<QPair<int, QVariant> > KFileItemModel::groups() const case SizeRole: m_groups = sizeRoleGroups(); break; case DateRole: m_groups = dateRoleGroups(); break; case PermissionsRole: m_groups = permissionRoleGroups(); break; - case OwnerRole: m_groups = genericStringRoleGroups("owner"); break; - case GroupRole: m_groups = genericStringRoleGroups("group"); break; - case TypeRole: m_groups = genericStringRoleGroups("type"); break; - case DestinationRole: m_groups = genericStringRoleGroups("destination"); break; - case PathRole: m_groups = genericStringRoleGroups("path"); break; - case CommentRole: m_groups = genericStringRoleGroups("comment"); break; - case TagsRole: m_groups = genericStringRoleGroups("tags"); break; case RatingRole: m_groups = ratingRoleGroups(); break; - case NoRole: break; - case IsDirRole: break; - case IsExpandedRole: break; - case ExpandedParentsCountRole: break; - default: Q_ASSERT(false); break; + default: m_groups = genericStringRoleGroups(sortRole()); break; } #ifdef KFILEITEMMODEL_DEBUG @@ -558,6 +547,8 @@ QList<KFileItemModel::RoleInfo> KFileItemModel::rolesInformation() info.role = map[i].role; info.translation = map[i].roleTranslation; info.group = map[i].groupTranslation; + info.requiresNepomuk = map[i].requiresNepomuk; + info.requiresIndexer = map[i].requiresIndexer; rolesInfo.append(info); } } @@ -1344,6 +1335,15 @@ int KFileItemModel::sortRoleCompare(const ItemData* a, const ItemData* b) const break; } + case ImageSizeRole: { + // Alway use a natural comparing to interpret the numbers of a string like + // "1600 x 1200" for having a correct sorting. + result = KStringHandler::naturalCompare(a->values.value("imageSize").toString(), + b->values.value("imageSize").toString(), + Qt::CaseSensitive); + break; + } + case PermissionsRole: case OwnerRole: case GroupRole: @@ -1953,29 +1953,29 @@ KFileItemList KFileItemModel::childItems(const KFileItem& item) const const KFileItemModel::RoleInfoMap* KFileItemModel::rolesInfoMap(int& count) { static const RoleInfoMap rolesInfoMap[] = { - // role roleType role translation group translation - { 0, NoRole, 0, 0, 0, 0 }, - { "name", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0 }, - { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0 }, - { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0 }, - { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0 }, - { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), 0, 0 }, - { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), 0, 0 }, - { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), 0, 0 }, - { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document") }, - { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document") }, - { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image") }, - { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image") }, - { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Music") }, - { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Music") }, - { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Music") }, - { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Music") }, - { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other") }, - { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other") }, - { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other") }, - { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other") }, - { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other") }, - { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "Group"), I18N_NOOP2_NOSTRIP("@label", "Other") }, + // | role | roleType | role translation | group translation | requires Nepomuk | requires indexer + { 0, NoRole, 0, 0, 0, 0, false, false }, + { "name", NameRole, I18N_NOOP2_NOSTRIP("@label", "Name"), 0, 0, false, false }, + { "size", SizeRole, I18N_NOOP2_NOSTRIP("@label", "Size"), 0, 0, false, false }, + { "date", DateRole, I18N_NOOP2_NOSTRIP("@label", "Date"), 0, 0, false, false }, + { "type", TypeRole, I18N_NOOP2_NOSTRIP("@label", "Type"), 0, 0, false, false }, + { "rating", RatingRole, I18N_NOOP2_NOSTRIP("@label", "Rating"), 0, 0, true, false }, + { "tags", TagsRole, I18N_NOOP2_NOSTRIP("@label", "Tags"), 0, 0, true, false }, + { "comment", CommentRole, I18N_NOOP2_NOSTRIP("@label", "Comment"), 0, 0, true, false }, + { "wordCount", WordCountRole, I18N_NOOP2_NOSTRIP("@label", "Word Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, + { "lineCount", LineCountRole, I18N_NOOP2_NOSTRIP("@label", "Line Count"), I18N_NOOP2_NOSTRIP("@label", "Document"), true, true }, + { "imageSize", ImageSizeRole, I18N_NOOP2_NOSTRIP("@label", "Image Size"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "orientation", OrientationRole, I18N_NOOP2_NOSTRIP("@label", "Orientation"), I18N_NOOP2_NOSTRIP("@label", "Image"), true, true }, + { "artist", ArtistRole, I18N_NOOP2_NOSTRIP("@label", "Artist"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, + { "album", AlbumRole, I18N_NOOP2_NOSTRIP("@label", "Album"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, + { "duration", DurationRole, I18N_NOOP2_NOSTRIP("@label", "Duration"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, + { "track", TrackRole, I18N_NOOP2_NOSTRIP("@label", "Track"), I18N_NOOP2_NOSTRIP("@label", "Music"), true, true }, + { "path", PathRole, I18N_NOOP2_NOSTRIP("@label", "Path"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "destination", DestinationRole, I18N_NOOP2_NOSTRIP("@label", "Link Destination"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "copiedFrom", CopiedFromRole, I18N_NOOP2_NOSTRIP("@label", "Copied From"), I18N_NOOP2_NOSTRIP("@label", "Other"), true, false }, + { "permissions", PermissionsRole, I18N_NOOP2_NOSTRIP("@label", "Permissions"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "owner", OwnerRole, I18N_NOOP2_NOSTRIP("@label", "Owner"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, + { "group", GroupRole, I18N_NOOP2_NOSTRIP("@label", "User Group"), I18N_NOOP2_NOSTRIP("@label", "Other"), false, false }, }; count = sizeof(rolesInfoMap) / sizeof(RoleInfoMap); diff --git a/src/kitemviews/kfileitemmodel.h b/src/kitemviews/kfileitemmodel.h index 0b1885a8c..4824dec3d 100644 --- a/src/kitemviews/kfileitemmodel.h +++ b/src/kitemviews/kfileitemmodel.h @@ -162,8 +162,16 @@ public: { QByteArray role; QString translation; QString group; + bool requiresNepomuk; + bool requiresIndexer; }; + /** + * @return Provides static information for all available roles that + * are supported by KFileItemModel. Some roles can only be + * determined if Nepomuk is enabled and/or the Nepomuk + * indexing is enabled. + */ static QList<RoleInfo> rolesInformation(); signals: @@ -344,6 +352,8 @@ private: const char* const roleTranslation; const char* const groupTranslationContext; const char* const groupTranslation; + const bool requiresNepomuk; + const bool requiresIndexer; }; /** diff --git a/src/kitemviews/kfileitemmodelrolesupdater.cpp b/src/kitemviews/kfileitemmodelrolesupdater.cpp index 7b0d75dba..efcf6b4cc 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.cpp +++ b/src/kitemviews/kfileitemmodelrolesupdater.cpp @@ -33,6 +33,10 @@ #include <QElapsedTimer> #include <QTimer> +#ifdef HAVE_NEPOMUK + #include "knepomukrolesprovider_p.h" +#endif + // Required includes for subItemsCount(): #ifdef Q_WS_WIN #include <QDir> @@ -74,6 +78,10 @@ KFileItemModelRolesUpdater::KFileItemModelRolesUpdater(KFileItemModel* model, QO m_previewJobs(), m_changedItemsTimer(0), m_changedItems() + #ifdef HAVE_NEPOMUK + , m_resolveNepomukRoles(false) + #endif + { Q_ASSERT(model); @@ -219,6 +227,23 @@ void KFileItemModelRolesUpdater::setRoles(const QSet<QByteArray>& roles) if (m_roles != roles) { m_roles = roles; +#ifdef HAVE_NEPOMUK + // Check whether there is at least one role that must be resolved + // with the help of Nepomuk. If this is the case, m_resolveNepomukRoles + // will be set to true and the (quite expensive) resolving will be done + // in KFileItemModelRolesUpdater::rolesData(). + const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance(); + m_resolveNepomukRoles = false; + QSetIterator<QByteArray> it(roles); + while (it.hasNext()) { + const QByteArray& role = it.next(); + if (rolesProvider.isNepomukRole(role)) { + m_resolveNepomukRoles = true; + break; + } + } +#endif + if (m_paused) { m_rolesChangedDuringPausing = true; } else { @@ -754,6 +779,17 @@ QHash<QByteArray, QVariant> KFileItemModelRolesUpdater::rolesData(const KFileIte data.insert("iconOverlays", item.overlays()); +#ifdef HAVE_NEPOMUK + if (m_resolveNepomukRoles) { + const KNepomukRolesProvider& rolesProvider = KNepomukRolesProvider::instance(); + QHashIterator<QByteArray, QVariant> it(rolesProvider.roleValues(item.url(), m_roles)); + while (it.hasNext()) { + it.next(); + data.insert(it.key(), it.value()); + } + } +#endif + return data; } diff --git a/src/kitemviews/kfileitemmodelrolesupdater.h b/src/kitemviews/kfileitemmodelrolesupdater.h index 4db2dde97..47255b03f 100644 --- a/src/kitemviews/kfileitemmodelrolesupdater.h +++ b/src/kitemviews/kfileitemmodelrolesupdater.h @@ -20,6 +20,7 @@ #ifndef KFILEITEMMODELROLESUPDATER_H #define KFILEITEMMODELROLESUPDATER_H +#include <config-nepomuk.h> #include <libdolphin_export.h> #include <KFileItem> @@ -204,6 +205,13 @@ private: // of time. QTimer* m_changedItemsTimer; QSet<KFileItem> m_changedItems; + +#ifdef HAVE_NEPOMUK + // True if roles must be resolved with the help of Nepomuk inside + // KFileItemModelRolesUpdater::rolesData(). + bool m_resolveNepomukRoles; +#endif + }; #endif diff --git a/src/kitemviews/kitemlistheaderwidget.cpp b/src/kitemviews/kitemlistheaderwidget.cpp index d3a8f001b..2105b674c 100644 --- a/src/kitemviews/kitemlistheaderwidget.cpp +++ b/src/kitemviews/kitemlistheaderwidget.cpp @@ -34,6 +34,7 @@ KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) : QGraphicsWidget(parent), m_automaticColumnResizing(true), m_model(0), + m_offset(0), m_columns(), m_columnWidths(), m_preferredColumnWidths(), @@ -48,10 +49,6 @@ KItemListHeaderWidget::KItemListHeaderWidget(QGraphicsWidget* parent) : m_movingRole.index = -1; setAcceptHoverEvents(true); - - QStyleOptionHeader option; - const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, &option, QSize()); - resize(0, headerSize.height()); } KItemListHeaderWidget::~KItemListHeaderWidget() @@ -142,6 +139,19 @@ qreal KItemListHeaderWidget::preferredColumnWidth(const QByteArray& role) const return m_preferredColumnWidths.value(role); } +void KItemListHeaderWidget::setOffset(qreal offset) +{ + if (m_offset != offset) { + m_offset = offset; + update(); + } +} + +qreal KItemListHeaderWidget::offset() const +{ + return m_offset; +} + qreal KItemListHeaderWidget::minimumColumnWidth() const { QFontMetricsF fontMetrics(font()); @@ -161,7 +171,7 @@ void KItemListHeaderWidget::paint(QPainter* painter, const QStyleOptionGraphicsI painter->setFont(font()); painter->setPen(palette().text().color()); - qreal x = 0; + qreal x = -m_offset; int orderIndex = 0; foreach (const QByteArray& role, m_columns) { const qreal roleWidth = m_columnWidths.value(role); @@ -265,7 +275,7 @@ void KItemListHeaderWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* event) } else { m_movingRole.pixmap = createRolePixmap(roleIndex); - qreal roleX = 0; + qreal roleX = -m_offset; for (int i = 0; i < roleIndex; ++i) { const QByteArray role = m_columns[i]; roleX += m_columnWidths.value(role); @@ -429,7 +439,7 @@ int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const { int index = -1; - qreal x = 0; + qreal x = -m_offset; foreach (const QByteArray& role, m_columns) { ++index; x += m_columnWidths.value(role); @@ -443,7 +453,7 @@ int KItemListHeaderWidget::roleIndexAt(const QPointF& pos) const bool KItemListHeaderWidget::isAboveRoleGrip(const QPointF& pos, int roleIndex) const { - qreal x = 0; + qreal x = -m_offset; for (int i = 0; i <= roleIndex; ++i) { const QByteArray role = m_columns[i]; x += m_columnWidths.value(role); @@ -484,7 +494,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const const int movingRight = movingLeft + movingWidth - 1; int targetIndex = 0; - qreal targetLeft = 0; + qreal targetLeft = -m_offset; while (targetIndex < m_columns.count()) { const QByteArray role = m_columns[targetIndex]; const qreal targetWidth = m_columnWidths.value(role); @@ -510,7 +520,7 @@ int KItemListHeaderWidget::targetOfMovingRole() const qreal KItemListHeaderWidget::roleXPosition(const QByteArray& role) const { - qreal x = 0; + qreal x = -m_offset; foreach (const QByteArray& visibleRole, m_columns) { if (visibleRole == role) { return x; diff --git a/src/kitemviews/kitemlistheaderwidget_p.h b/src/kitemviews/kitemlistheaderwidget_p.h index e27106066..f8bba977b 100644 --- a/src/kitemviews/kitemlistheaderwidget_p.h +++ b/src/kitemviews/kitemlistheaderwidget_p.h @@ -59,6 +59,9 @@ public: void setPreferredColumnWidth(const QByteArray& role, qreal width); qreal preferredColumnWidth(const QByteArray& role) const; + void setOffset(qreal offset); + qreal offset() const; + qreal minimumColumnWidth() const; virtual void paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget = 0); @@ -144,6 +147,7 @@ private: bool m_automaticColumnResizing; KItemModelBase* m_model; + qreal m_offset; QList<QByteArray> m_columns; QHash<QByteArray, qreal> m_columnWidths; QHash<QByteArray, qreal> m_preferredColumnWidths; diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 8a18991a0..ddc65c387 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -237,7 +237,7 @@ void KItemListView::setItemOffset(qreal offset) m_layouter->setItemOffset(offset); if (m_headerWidget->isVisible()) { - m_headerWidget->setPos(-offset, 0); + m_headerWidget->setOffset(offset); } // Don't check whether the m_layoutTimer is active: Changing the @@ -418,6 +418,7 @@ void KItemListView::setGeometry(const QRectF& rect) const QSizeF newSize = rect.size(); if (m_itemSize.isEmpty()) { + m_headerWidget->resize(rect.width(), m_headerWidget->size().height()); if (m_headerWidget->automaticColumnResizing()) { applyAutomaticColumnWidths(); } else { @@ -425,7 +426,6 @@ void KItemListView::setGeometry(const QRectF& rect) const QSizeF dynamicItemSize(qMax(newSize.width(), requiredWidth), m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); - m_headerWidget->resize(dynamicItemSize.width(), m_headerWidget->size().height()); } // Triggering a synchronous layout is fine from a performance point of view, @@ -604,10 +604,18 @@ bool KItemListView::isTransactionActive() const return m_activeTransactions > 0; } +// TODO: +#include <QGraphicsScene> +#include <QGraphicsView> void KItemListView::setHeaderVisible(bool visible) { if (visible && !m_headerWidget->isVisible()) { - m_headerWidget->setPos(0, 0); + QStyleOptionHeader option; + const QSize headerSize = style()->sizeFromContents(QStyle::CT_HeaderSection, + &option, QSize()); + + m_headerWidget->setPos(0, 0); + m_headerWidget->resize(size().width(), headerSize.height()); m_headerWidget->setModel(m_model); m_headerWidget->setColumns(m_visibleRoles); m_headerWidget->setZValue(1); @@ -621,7 +629,7 @@ void KItemListView::setHeaderVisible(bool visible) connect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), this, SIGNAL(sortRoleChanged(QByteArray,QByteArray))); - m_layouter->setHeaderHeight(m_headerWidget->size().height()); + m_layouter->setHeaderHeight(headerSize.height()); m_headerWidget->setVisible(true); } else if (!visible && m_headerWidget->isVisible()) { disconnect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), @@ -1898,6 +1906,19 @@ QHash<QByteArray, qreal> KItemListView::preferredColumnWidths(const KItemRangeLi QHash<QByteArray, qreal> widths; + // Calculate the minimum width for each column that is required + // to show the headline unclipped. + const QFontMetricsF fontMetrics(m_headerWidget->font()); + const int gripMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderGripMargin); + const int headerMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderMargin); + foreach (const QByteArray& visibleRole, visibleRoles()) { + const QString headerText = m_model->roleDescription(visibleRole); + const qreal headerWidth = fontMetrics.width(headerText) + gripMargin + headerMargin * 2; + widths.insert(visibleRole, headerWidth); + } + + // Calculate the preferred column withs for each item and ignore values + // smaller than the width for showing the headline unclipped. int calculatedItemCount = 0; bool maxTimeExceeded = false; foreach (const KItemRange& itemRange, itemRanges) { @@ -1936,7 +1957,6 @@ void KItemListView::applyColumnWidthsFromHeader() const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth), m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); - m_headerWidget->resize(dynamicItemSize.width(), m_headerWidget->size().height()); // Update the role sizes for all visible widgets QHashIterator<int, KItemListWidget*> it(m_visibleItems); @@ -2001,13 +2021,8 @@ void KItemListView::updatePreferredColumnWidths(const KItemRangeList& itemRanges void KItemListView::updatePreferredColumnWidths() { - if (!m_model) { - return; - } - - const int itemCount = m_model->count(); - if (itemCount > 0) { - updatePreferredColumnWidths(KItemRangeList() << KItemRange(0, itemCount)); + if (m_model) { + updatePreferredColumnWidths(KItemRangeList() << KItemRange(0, m_model->count())); } } @@ -2056,7 +2071,6 @@ void KItemListView::applyAutomaticColumnWidths() dynamicItemSize.rwidth() = qMax(requiredWidth, availableWidth); m_layouter->setItemSize(dynamicItemSize); - m_headerWidget->resize(dynamicItemSize.width(), m_headerWidget->size().height()); // Update the role sizes for all visible widgets QHashIterator<int, KItemListWidget*> it(m_visibleItems); diff --git a/src/kitemviews/knepomukrolesprovider.cpp b/src/kitemviews/knepomukrolesprovider.cpp new file mode 100644 index 000000000..1612d57b6 --- /dev/null +++ b/src/kitemviews/knepomukrolesprovider.cpp @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#include "knepomukrolesprovider_p.h" + +#include <KDebug> +#include <KGlobal> + +#include <Nepomuk/Resource> +#include <Nepomuk/Tag> +#include <Nepomuk/Types/Property> +#include <Nepomuk/Variant> + +struct KNepomukRolesProviderSingleton +{ + KNepomukRolesProvider instance; +}; +K_GLOBAL_STATIC(KNepomukRolesProviderSingleton, s_nepomukRolesProvider) + + +KNepomukRolesProvider& KNepomukRolesProvider::instance() +{ + return s_nepomukRolesProvider->instance; +} + +KNepomukRolesProvider::~KNepomukRolesProvider() +{ +} + +bool KNepomukRolesProvider::isNepomukRole(const QByteArray& role) const +{ + return m_roles.contains(role); +} + +QHash<QByteArray, QVariant> KNepomukRolesProvider::roleValues(const QUrl& url, + const QSet<QByteArray>& roles) const +{ + const Nepomuk::Resource resource(url); + if (!resource.isValid()) { + return QHash<QByteArray, QVariant>(); + } + + QHash<QByteArray, QVariant> values; + + int width = -1; + int height = -1; + + QHashIterator<QUrl, Nepomuk::Variant> it(resource.properties()); + while (it.hasNext()) { + it.next(); + + const Nepomuk::Types::Property property = it.key(); + const QByteArray role = m_roleForUri.value(property.uri()); + if (role.isEmpty() || !roles.contains(role)) { + continue; + } + + const Nepomuk::Variant value = it.value(); + + if (role == "imageSize") { + // Merge the two Nepomuk properties for width and height + // as one string into the "imageSize" role + const QString uri = property.uri().toString(); + if (uri.endsWith("#width")) { + width = value.toInt(); + } else if (uri.endsWith("#height")) { + height = value.toInt(); + } + + if (width >= 0 && height >= 0) { + const QString widthAndHeight = QString::number(width) + + QLatin1String(" x ") + + QString::number(height); + values.insert(role, widthAndHeight); + } + } else if (role == "tags") { + const QString tags = tagsFromValues(value.toStringList()); + values.insert(role, tags); + } else { + values.insert(role, value.toString()); + } + } + + return values; +} + +KNepomukRolesProvider::KNepomukRolesProvider() : + m_roles(), + m_roleForUri() +{ + struct UriInfo + { + const char* const uri; + const char* const role; + }; + + // Mapping from the URIs to the KFileItemModel roles. Note that this must not be + // a 1:1 mapping: One role may contain several URI-values (e.g. the URIs for height and + // width of an image are mapped to the role "imageSize") + static const UriInfo uriInfoList[] = { + { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#numericRating", "rating" }, + { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#hasTag", "tags" }, + { "http://www.semanticdesktop.org/ontologies/2007/08/15/nao#description", "comment" }, + { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#wordCount", "wordCount" }, + { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#lineCount", "lineCount" }, + { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#width", "imageSize" }, + { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#height", "imageSize" }, + { "http://www.semanticdesktop.org/ontologies/2007/05/10/nexif#orientation", "orientation", }, + { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#performer", "artist" }, + { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#musicAlbum", "album" }, + { "http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#duration", "duration" }, + { "http://www.semanticdesktop.org/ontologies/2009/02/19/nmm#trackNumber", "track" }, + { "http://www.semanticdesktop.org/ontologies/2010/04/30/ndo#copiedFrom", "copiedFrom" } + }; + + for (unsigned int i = 0; i < sizeof(uriInfoList) / sizeof(UriInfo); ++i) { + m_roleForUri.insert(QUrl(uriInfoList[i].uri), uriInfoList[i].role); + m_roles.insert(uriInfoList[i].role); + } +} + +QString KNepomukRolesProvider::tagsFromValues(const QStringList& values) const +{ + QString tags; + + for (int i = 0; i < values.count(); ++i) { + if (i > 0) { + tags.append(QLatin1String(", ")); + } + + const Nepomuk::Tag tag(values[i]); + tags += tag.genericLabel(); + } + + return tags; +} + diff --git a/src/kitemviews/knepomukrolesprovider_p.h b/src/kitemviews/knepomukrolesprovider_p.h new file mode 100644 index 000000000..795509333 --- /dev/null +++ b/src/kitemviews/knepomukrolesprovider_p.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2012 by Peter Penz <[email protected]> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * + ***************************************************************************/ + +#ifndef KNEPOMUKROLESPROVIDER_H +#define KNEPOMUKROLESPROVIDER_H + +#include <QHash> +#include <QSet> +#include <QUrl> + +class QUrl; + +/** + * @brief Allows accessing metadata of a file by providing KFileItemModel roles. + * + * Is a helper class for KFileItemModelRolesUpdater to retrieve roles that + * are only accessible with Nepomuk. + */ +class KNepomukRolesProvider +{ +public: + static KNepomukRolesProvider& instance(); + virtual ~KNepomukRolesProvider(); + + /** + * @return True if the values of the role can be determined by Nepomuk. + */ + bool isNepomukRole(const QByteArray& role) const; + + /** + * @return Values for the roles \a roles that can be determined from the file + * with the URL \a url. + */ + QHash<QByteArray, QVariant> roleValues(const QUrl& url, const QSet<QByteArray>& roles) const; + +protected: + KNepomukRolesProvider(); + +private: + QString tagsFromValues(const QStringList& values) const; + +private: + QSet<QByteArray> m_roles; + QHash<QUrl, QByteArray> m_roleForUri; + + friend class KNepomukRolesProviderSingleton; +}; + +#endif + |
