diff --git a/applications/imageviewer/package/contents/ui/Browser.qml b/applications/imageviewer/package/contents/ui/Browser.qml index 363f4d10..b21da3fd 100644 --- a/applications/imageviewer/package/contents/ui/Browser.qml +++ b/applications/imageviewer/package/contents/ui/Browser.qml @@ -1,135 +1,136 @@ /* * Copyright 2011 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ import QtQuick 1.0 import org.kde.metadatamodels 0.1 as MetadataModels import org.kde.plasma.components 0.1 as PlasmaComponents import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.plasma.mobilecomponents 0.1 as MobileComponents import org.kde.plasma.slccomponents 0.1 as SlcComponents import org.kde.qtextracomponents 0.1 PlasmaComponents.Page { anchors { fill: parent topMargin: toolBar.height } tools: Item { width: parent.width height: childrenRect.height PlasmaCore.DataSource { id: hotplugSource engine: "hotplug" connectedSources: sources } PlasmaCore.DataSource { id: devicesSource engine: "soliddevice" connectedSources: hotplugSource.sources } PlasmaCore.DataModel { id: devicesModel dataSource: hotplugSource } Row { anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter spacing: 8 opacity: (imageViewer.state == "browsing") ? 1 : 0 MobileComponents.IconButton { icon: QIcon("drive-harddisk") opacity: resultsGrid.model == metadataModel ? 0.2 : 1 width: 48 height: 48 onClicked: { resultsGrid.model = metadataModel } } Repeater { model: devicesModel MobileComponents.IconButton { id: deviceButton icon: QIcon(model["icon"]) //FIXME: use the declarative branch in workspace that tells about removable visible: devicesSource.data[udi]["Removable"] == true opacity: (dirModel.url == devicesSource.data[udi]["File Path"] && resultsGrid.model == dirModel) ? 1 : 0.2 width: 48 height: 48 onClicked: { dirModel.url = devicesSource.data[udi]["File Path"] resultsGrid.model = dirModel } } } } MobileComponents.ViewSearch { id: searchBox anchors { left: parent.left right: parent.right verticalCenter: parent.verticalCenter } onSearchQueryChanged: { metadataModel.extraParameters["nfo:fileName"] = searchBox.searchQuery } } } MobileComponents.IconGrid { id: resultsGrid anchors.fill: parent model: PlasmaCore.SortFilterModel { sourceModel: metadataModel sortRole: "label" sortOrder: "AscendingOrder" + sortCaseSensitivity: Qt.CaseInsensitive } delegateWidth: 130 delegateHeight: 120 delegate: MobileComponents.ResourceDelegate { id: resourceDelegate className: model["className"]?model["className"]:"Image" width: 130 height: 120 infoLabelVisible: false property string label: model["label"]?model["label"]:model["display"] onPressAndHold: { resourceInstance.uri = model["url"]?model["url"]:model["resourceUri"] resourceInstance.title = model["label"] } onClicked: { if (mimeType == "inode/directory") { dirModel.url = model["url"] resultsGrid.model = dirModel } else if (!mainStack.busy) { loadImage(model["url"]) } } } } } diff --git a/applications/imageviewer/package/contents/ui/imageviewer.qml b/applications/imageviewer/package/contents/ui/imageviewer.qml index 88c00850..22487731 100644 --- a/applications/imageviewer/package/contents/ui/imageviewer.qml +++ b/applications/imageviewer/package/contents/ui/imageviewer.qml @@ -1,106 +1,110 @@ /* * Copyright 2011 Marco Martin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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. */ import QtQuick 1.0 import org.kde.metadatamodels 0.1 as MetadataModels import org.kde.plasma.components 0.1 as PlasmaComponents import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.plasma.mobilecomponents 0.1 as MobileComponents import org.kde.plasma.slccomponents 0.1 as SlcComponents import org.kde.qtextracomponents 0.1 Image { id: imageViewer objectName: "imageViewer" source: "image://appbackgrounds/standard" fillMode: Image.Tile state: "browsing" width: 360 height: 360 signal zoomIn signal zoomOut MobileComponents.Package { id: viewerPackage name: "org.kde.active.imageviewer" } MobileComponents.ResourceInstance { id: resourceInstance } function loadImage(path) { if (path.length == 0) { return } var viewerItem = mainStack.push(Qt.createComponent("ViewerPage.qml")) viewerItem.loadImage(path) } Timer { id: firstRunTimer interval: 300 repeat: false onTriggered: { loadImage(startupArguments[0]) + + // sort by column 0 (called "label" in metadatamodel.cpp), + // that is, the file name. + metadataModel.sort(0) } } MetadataModels.MetadataUserTypes { id: userTypes } MetadataModels.MetadataModel { id: metadataModel resourceType: "nfo:Image" sortBy: [userTypes.sortFields[resourceType]] sortOrder: Qt.AscendingOrder property bool starting: true onStatusChanged: { if (status == MetadataModels.MetadataModel.Idle && starting) { firstRunTimer.running = true starting = false } } } PlasmaComponents.ToolBar { id: toolBar } PlasmaComponents.PageStack { id: mainStack clip: false toolBar: toolBar initialPage: Qt.createComponent("Browser.qml") anchors.fill: parent } SlcComponents.SlcMenu { id: contextMenu } } diff --git a/components/metadatamodel/metadatamodel.cpp b/components/metadatamodel/metadatamodel.cpp index 9632dda6..fb96061c 100644 --- a/components/metadatamodel/metadatamodel.cpp +++ b/components/metadatamodel/metadatamodel.cpp @@ -1,672 +1,682 @@ /* Copyright 2011 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "metadatamodel.h" #include "resourcewatcher.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kext.h" MetadataModel::MetadataModel(QObject *parent) : AbstractMetadataModel(parent), m_queryClient(0), m_screenshotSize(180, 120) { m_queryTimer = new QTimer(this); m_queryTimer->setSingleShot(true); connect(m_queryTimer, SIGNAL(timeout()), this, SLOT(doQuery())); m_newEntriesTimer = new QTimer(this); m_newEntriesTimer->setSingleShot(true); connect(m_newEntriesTimer, SIGNAL(timeout()), this, SLOT(newEntriesDelayed())); m_previewTimer = new QTimer(this); m_previewTimer->setSingleShot(true); connect(m_previewTimer, SIGNAL(timeout()), this, SLOT(delayedPreview())); //using the same cache of the engine, they index both by url m_imageCache = new KImageCache("plasma_engine_preview", 10485760); m_watcher = new Nepomuk::ResourceWatcher(this); m_watcher->addProperty(QUrl("http://www.semanticdesktop.org/ontologies/2007/08/15/nao#numericRating")); connect(m_watcher, SIGNAL(propertyAdded(Nepomuk::Resource, Nepomuk::Types::Property, QVariant)), this, SLOT(propertyChanged(Nepomuk::Resource, Nepomuk::Types::Property, QVariant))); QHash roleNames; roleNames[Label] = "label"; roleNames[Description] = "description"; roleNames[Types] = "types"; roleNames[ClassName] = "className"; roleNames[GenericClassName] = "genericClassName"; roleNames[HasSymbol] = "hasSymbol"; roleNames[Icon] = "icon"; roleNames[Thumbnail] = "thumbnail"; roleNames[IsFile] = "isFile"; roleNames[Exists] = "exists"; roleNames[Rating] = "rating"; roleNames[NumericRating] = "numericRating"; roleNames[Symbols] = "symbols"; roleNames[ResourceUri] = "resourceUri"; roleNames[ResourceType] = "resourceType"; roleNames[MimeType] = "mimeType"; roleNames[Url] = "url"; roleNames[Topics] = "topics"; roleNames[TopicsNames] = "topicsNames"; roleNames[Tags] = "tags"; roleNames[TagsNames] = "tagsNames"; setRoleNames(roleNames); } MetadataModel::~MetadataModel() { delete m_imageCache; } void MetadataModel::setQuery(const Nepomuk::Query::Query &query) { m_queryTimer->stop(); m_query = query; if (Nepomuk::Query::QueryServiceClient::serviceAvailable()) { doQuery(); } } Nepomuk::Query::Query MetadataModel::query() const { return m_query; } void MetadataModel::setQueryString(const QString &query) { if (query == m_queryString) { return; } m_queryString = query; m_queryTimer->start(0); emit queryStringChanged(); } QString MetadataModel::queryString() const { return m_queryString; } void MetadataModel::setSortBy(const QVariantList &sortBy) { QStringList stringList = variantToStringList(sortBy); if (m_sortBy == stringList) { return; } m_sortBy = stringList; m_queryTimer->start(0); emit sortByChanged(); } QVariantList MetadataModel::sortBy() const { return stringToVariantList(m_sortBy); } void MetadataModel::setSortOrder(Qt::SortOrder sortOrder) { if (m_sortOrder == sortOrder) { return; } m_sortOrder = sortOrder; m_queryTimer->start(0); emit sortOrderChanged(); } Qt::SortOrder MetadataModel::sortOrder() const { return m_sortOrder; } int MetadataModel::find(const QString &resourceUri) { int index = -1; int i = 0; Nepomuk::Resource resToFind(resourceUri); foreach (const Nepomuk::Resource &res, m_resources) { if (res == resToFind) { index = i; break; } ++i; } return index; } void MetadataModel::doQuery() { QDeclarativePropertyMap *parameters = qobject_cast(extraParameters()); //check if really all properties to build the query are null if (m_queryString.isEmpty() && resourceType().isEmpty() && mimeType().isEmpty() && activityId().isEmpty() && tagStrings().size() == 0 && !startDate().isValid() && !endDate().isValid() && minimumRating() <= 0 && maximumRating() <= 0 && parameters->size() == 0) { return; } setStatus(Waiting); m_query = Nepomuk::Query::Query(); m_query.setQueryFlags(Nepomuk::Query::Query::WithoutFullTextExcerpt); Nepomuk::Query::AndTerm rootTerm; if (!m_queryString.isEmpty()) { rootTerm.addSubTerm(Nepomuk::Query::QueryParser::parseQuery(m_queryString).term()); } if (!resourceType().isEmpty()) { //FIXME: more elegant QString type = resourceType(); bool negation = false; if (type.startsWith("!")) { type = type.remove(0, 1); negation = true; } if (negation) { rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(Nepomuk::Query::ResourceTypeTerm(propertyUrl(type)))); } else { rootTerm.addSubTerm(Nepomuk::Query::ResourceTypeTerm(propertyUrl(type))); if (type != "nfo:Bookmark") { //FIXME: remove bookmarks if not explicitly asked for rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(Nepomuk::Query::ResourceTypeTerm(propertyUrl("nfo:Bookmark")))); } } } if (!mimeType().isEmpty()) { QString type = mimeType(); bool negation = false; if (type.startsWith("!")) { type = type.remove(0, 1); negation = true; } Nepomuk::Query::ComparisonTerm term(Nepomuk::Vocabulary::NIE::mimeType(), Nepomuk::Query::LiteralTerm(type)); if (negation) { rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(term)); } else { rootTerm.addSubTerm(term); } } if (parameters && parameters->size() > 0) { foreach (const QString &key, parameters->keys()) { QString parameter = parameters->value(key).toString(); bool negation = false; if (parameter.startsWith("!")) { parameter = parameter.remove(0, 1); negation = true; } //FIXME: Contains should work, but doesn't match for file names Nepomuk::Query::ComparisonTerm term(propertyUrl(key), Nepomuk::Query::LiteralTerm(parameter), Nepomuk::Query::ComparisonTerm::Regexp); if (negation) { rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(term)); } else { rootTerm.addSubTerm(term); } } } if (!activityId().isEmpty()) { QString activity = activityId(); bool negation = false; if (activity.startsWith("!")) { activity = activity.remove(0, 1); negation = true; } kDebug() << "Asking for resources of activity" << activityId(); Nepomuk::Resource acRes(activity, Nepomuk::Vocabulary::KEXT::Activity()); Nepomuk::Query::ComparisonTerm term(Soprano::Vocabulary::NAO::isRelated(), Nepomuk::Query::ResourceTerm(acRes)); term.setInverted(true); if (negation) { rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(term)); } else { rootTerm.addSubTerm(term); } } foreach (const QString &tag, tagStrings()) { QString individualTag = tag; bool negation = false; if (individualTag.startsWith("!")) { individualTag = individualTag.remove(0, 1); negation = true; } Nepomuk::Query::ComparisonTerm term( Soprano::Vocabulary::NAO::hasTag(), Nepomuk::Query::LiteralTerm(individualTag)); if (negation) { rootTerm.addSubTerm(Nepomuk::Query::NegationTerm::negateTerm(term)); } else { rootTerm.addSubTerm(term); } } if (startDate().isValid() || endDate().isValid()) { rootTerm.addSubTerm(Nepomuk::Query::dateRangeQuery(startDate(), endDate()).term()); } if (minimumRating() > 0) { const Nepomuk::Query::LiteralTerm ratingTerm(minimumRating()); Nepomuk::Query::ComparisonTerm term = Nepomuk::Types::Property(propertyUrl("nao:numericRating")) > ratingTerm; rootTerm.addSubTerm(term); } if (maximumRating() > 0) { const Nepomuk::Query::LiteralTerm ratingTerm(maximumRating()); Nepomuk::Query::ComparisonTerm term = Nepomuk::Types::Property(propertyUrl("nao:numericRating")) < ratingTerm; rootTerm.addSubTerm(term); } int weight = m_sortBy.length() + 1; foreach (const QString &sortProperty, m_sortBy) { Nepomuk::Query::ComparisonTerm sortTerm(propertyUrl(sortProperty), Nepomuk::Query::Term()); sortTerm.setSortWeight(weight, m_sortOrder); rootTerm.addSubTerm(sortTerm); --weight; } m_query.setTerm(rootTerm); kDebug()<<"Sparql query:"< &)), this, SLOT(newEntries(const QList &))); connect(m_queryClient, SIGNAL(entriesRemoved(const QList &)), this, SLOT(entriesRemoved(const QList &))); connect(m_queryClient, SIGNAL(finishedListing()), this, SLOT(finishedListing())); /*FIXME: safe without limit? if (limit > RESULT_LIMIT || limit <= 0) { m_query.setLimit(RESULT_LIMIT); } */ m_queryClient->query(m_query); } void MetadataModel::newEntries(const QList< Nepomuk::Query::Result > &entries) { setStatus(Running); foreach (Nepomuk::Query::Result res, entries) { //kDebug() << "Result!!!" << res.resource().genericLabel() << res.resource().type(); //kDebug() << "Result label:" << res.genericLabel(); m_resourcesToInsert << res.resource(); } if (!m_newEntriesTimer->isActive()) { m_newEntriesTimer->start(200); } } void MetadataModel::newEntriesDelayed() { beginInsertRows(QModelIndex(), m_resources.count(), m_resources.count()+m_resourcesToInsert.count()-1); m_watcher->stop(); foreach (Nepomuk::Resource res, m_resourcesToInsert) { //kDebug() << "Result!!!" << res.resource().genericLabel() << res.resource().type(); //kDebug() << "Result label:" << res.genericLabel(); m_uriToResourceIndex[res.resourceUri()] = m_resources.count(); m_resources << res; m_watcher->addResource(res); } m_watcher->start(); m_resourcesToInsert.clear(); endInsertRows(); emit countChanged(); } void MetadataModel::propertyChanged(Nepomuk::Resource res, Nepomuk::Types::Property prop, QVariant val) { Q_UNUSED(prop) Q_UNUSED(val) const int index = m_uriToResourceIndex.value(res.resourceUri()); if (index >= 0) { emit dataChanged(createIndex(index, 0, 0), createIndex(index, 0, 0)); } } void MetadataModel::entriesRemoved(const QList &urls) { int prevIndex = -100; //pack all the stuff to remove in groups, to emit the least possible signals //this assumes urls are in the same order they arrived ion the results //it's a map because we want to remove values from the vector in inverted order to keep indexes valid trough the remove loop QMap toRemove; foreach (const QUrl &url, urls) { const int index = m_uriToResourceIndex.value(url); if (index == prevIndex + 1) { toRemove[prevIndex]++; } else { toRemove[index] = 1; } prevIndex = index; } QMap::const_iterator i = toRemove.constEnd(); while (i != toRemove.constBegin()) { --i; beginRemoveRows(QModelIndex(), i.key(), i.key()+i.value()-1); m_resources.remove(i.key(), i.value()); endRemoveRows(); } //another loop, we don't depend to m_uriToResourceIndex in data(), but we take this doublesafety foreach (const QUrl &url, urls) { m_uriToResourceIndex.remove(url); } //FIXME: this loop makes all the optimizations useless, get rid either of it or the optimizations for (int i = 0; i < m_resources.count(); ++i) { m_uriToResourceIndex[m_resources[i].resourceUri()] = i; } emit countChanged(); } void MetadataModel::finishedListing() { setStatus(Idle); } QVariant MetadataModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.column() != 0 || index.row() < 0 || index.row() >= m_resources.count()){ return QVariant(); } const Nepomuk::Resource &resource = m_resources[index.row()]; switch (role) { case Qt::DisplayRole: case Label: return resource.genericLabel(); case Description: return resource.genericDescription(); case Types: { QStringList types; foreach (const QUrl &u, resource.types()) { types << u.toString(); } return types; } case ClassName: return resource.className(); case GenericClassName: { //FIXME: a more elegant way is needed QString genericClassName = resource.className(); //FIXME: most bookmarks are Document too, so Bookmark wins if (resource.types().contains(QUrl::fromEncoded("http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark"))) { return "Bookmark"; } Nepomuk::Types::Class resClass(resource.resourceType()); foreach (Nepomuk::Types::Class parentClass, resClass.parentClasses()) { if (parentClass.label() == "Document" || parentClass.label() == "Audio" || parentClass.label() == "Video" || parentClass.label() == "Image" || parentClass.label() == "Contact") { genericClassName = parentClass.label(); break; //two cases where the class is 2 levels behind the level of generalization we want } else if (parentClass.label() == "RasterImage") { genericClassName = "Image"; } else if (parentClass.label() == "TextDocument") { genericClassName = "Document"; } } return genericClassName; } case Qt::DecorationRole: { QString icon = resource.genericIcon(); if (icon.isEmpty() && resource.isFile()) { KUrl url = resource.toFile().url(); if (!url.isEmpty()) { icon = KMimeType::iconNameForUrl(url); } } if (icon.isEmpty()) { // use resource types to find a suitable icon. //TODO icon = retrieveIconName(QStringList(resource.className())); //kDebug() << "symbol" << icon; } if (icon.split(",").count() > 1) { kDebug() << "More than one icon!" << icon; icon = icon.split(",").last(); } return KIcon(icon); } case HasSymbol: case Icon: { QString icon = resource.genericIcon(); if (icon.isEmpty() && resource.isFile()) { KUrl url = resource.toFile().url(); if (!url.isEmpty()) { icon = KMimeType::iconNameForUrl(url); } } if (icon.isEmpty()) { // use resource types to find a suitable icon. //TODO icon = retrieveIconName(QStringList(resource.className())); //kDebug() << "symbol" << icon; } if (icon.split(",").count() > 1) { kDebug() << "More than one icon!" << icon; icon = icon.split(",").last(); } return icon; } case Thumbnail: if (resource.isFile() && resource.toFile().url().isLocalFile()) { KUrl file(resource.toFile().url()); QImage preview = QImage(m_screenshotSize, QImage::Format_ARGB32_Premultiplied); if (m_imageCache->findImage(file.prettyUrl(), &preview)) { return preview; } m_previewTimer->start(100); const_cast(this)->m_filesToPreview[file] = QPersistentModelIndex(index); } return QVariant(); case IsFile: return resource.isFile(); case Exists: return resource.exists(); case Rating: return resource.rating(); case NumericRating: return resource.property(QUrl("http://www.semanticdesktop.org/ontologies/2007/08/15/nao#numericRating")).toString(); case Symbols: return resource.symbols(); case ResourceUri: return resource.resourceUri(); case ResourceType: return resource.resourceType(); case MimeType: return resource.property(QUrl("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#mimeType")).toString(); case Url: { if (resource.isFile() && resource.toFile().url().isLocalFile()) { return resource.toFile().url().prettyUrl(); } else { return resource.property(QUrl("http://www.semanticdesktop.org/ontologies/2007/01/19/nie#url")).toString(); } } case Topics: { QStringList topics; foreach (const Nepomuk::Resource &u, resource.topics()) { topics << u.resourceUri().toString(); } return topics; } case TopicsNames: { QStringList topicNames; foreach (const Nepomuk::Resource &u, resource.topics()) { topicNames << u.genericLabel(); } return topicNames; } case Tags: { QStringList tags; foreach (const Nepomuk::Tag &tag, resource.tags()) { tags << tag.resourceUri().toString(); } return tags; } case TagsNames: { QStringList tagNames; foreach (const Nepomuk::Tag &tag, resource.tags()) { tagNames << tag.genericLabel(); } return tagNames; } default: return QVariant(); } } void MetadataModel::delayedPreview() { QHash::const_iterator i = m_filesToPreview.constBegin(); KFileItemList list; while (i != m_filesToPreview.constEnd()) { KUrl file = i.key(); QPersistentModelIndex index = i.value(); if (!m_previewJobs.contains(file) && file.isValid()) { list.append(KFileItem(file, QString(), 0)); m_previewJobs.insert(file, QPersistentModelIndex(index)); } ++i; } if (list.size() > 0) { KIO::PreviewJob* job = KIO::filePreview(list, m_screenshotSize); job->setIgnoreMaximumSize(true); kDebug() << "Created job" << job; connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)), this, SLOT(showPreview(const KFileItem&, const QPixmap&))); connect(job, SIGNAL(failed(const KFileItem&)), this, SLOT(previewFailed(const KFileItem&))); } m_filesToPreview.clear(); } void MetadataModel::showPreview(const KFileItem &item, const QPixmap &preview) { QPersistentModelIndex index = m_previewJobs.value(item.url()); m_previewJobs.remove(item.url()); if (!index.isValid()) { return; } m_imageCache->insertImage(item.url().prettyUrl(), preview.toImage()); //kDebug() << "preview size:" << preview.size(); emit dataChanged(index, index); } void MetadataModel::previewFailed(const KFileItem &item) { m_previewJobs.remove(item.url()); } +// Just signal QSortFilterProxyModel to do the real sorting. +void MetadataModel::sort(int column, Qt::SortOrder order) +{ + Q_UNUSED(column); + Q_UNUSED(order); + + beginResetModel(); + endResetModel(); +} + #include "metadatamodel.moc" diff --git a/components/metadatamodel/metadatamodel.h b/components/metadatamodel/metadatamodel.h index bfda4bcb..40c200e7 100644 --- a/components/metadatamodel/metadatamodel.h +++ b/components/metadatamodel/metadatamodel.h @@ -1,147 +1,155 @@ /* Copyright 2011 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef METADATAMODEL_H #define METADATAMODEL_H #include "abstractmetadatamodel.h" #include #include #include #include #include #include namespace Nepomuk { class ResourceWatcher; } class QDBusServiceWatcher; class QTimer; class KImageCache; class MetadataModel : public AbstractMetadataModel { Q_OBJECT Q_PROPERTY(QString queryString READ queryString WRITE setQueryString NOTIFY queryStringChanged) Q_PROPERTY(QVariantList sortBy READ sortBy WRITE setSortBy NOTIFY sortByChanged) Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder NOTIFY sortOrderChanged) public: enum Roles { Label = Qt::UserRole+1, Description, Types, ClassName, GenericClassName, HasSymbol, Icon, Thumbnail, IsFile, Exists, Rating, NumericRating, Symbols, ResourceUri, ResourceType, MimeType, Url, Topics, TopicsNames, Tags, TagsNames }; MetadataModel(QObject *parent = 0); ~MetadataModel(); void setQuery(const Nepomuk::Query::Query &query); Nepomuk::Query::Query query() const; virtual int count() const {return m_resources.count();} void setQueryString(const QString &query); QString queryString() const; void setSortBy(const QVariantList &sortBy); QVariantList sortBy() const; void setSortOrder(Qt::SortOrder sortOrder); Qt::SortOrder sortOrder() const; /** * searches for a resource in the whole model * @arg resToFind the uri or url of the resource */ Q_INVOKABLE int find(const QString &resToFind); //Reimplemented QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + /** + * Reimplemented + * Just signal QSortFilterProxyModel to do the real sorting. + * Use this class as parameter to QSortFilterProxyModel->setSourceModel (C++) or + * PlasmaCore.SortFilterModel.sourceModel (QML) to get the real sorting. + */ + Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + Q_SIGNALS: void queryStringChanged(); void sortByChanged(); void sortOrderChanged(); protected Q_SLOTS: void newEntries(const QList< Nepomuk::Query::Result > &entries); void entriesRemoved(const QList &urls); virtual void doQuery(); void newEntriesDelayed(); void finishedListing(); void propertyChanged(Nepomuk::Resource res, Nepomuk::Types::Property prop, QVariant val); void showPreview(const KFileItem &item, const QPixmap &preview); void previewFailed(const KFileItem &item); void delayedPreview(); private: Nepomuk::Query::Query m_query; Nepomuk::Query::QueryServiceClient *m_queryClient; Nepomuk::ResourceWatcher* m_watcher; QVector m_resources; QList m_resourcesToInsert; QHash m_uriToResourceIndex; QTimer *m_queryTimer; QTimer *m_newEntriesTimer; //pieces to build m_query QString m_queryString; QStringList m_sortBy; Qt::SortOrder m_sortOrder; //previews QTimer *m_previewTimer; QHash m_filesToPreview; QSize m_screenshotSize; QHash m_previewJobs; KImageCache* m_imageCache; }; #endif