diff --git a/containments/desktop/package/contents/config/main.xml b/containments/desktop/package/contents/config/main.xml
--- a/containments/desktop/package/contents/config/main.xml
+++ b/containments/desktop/package/contents/config/main.xml
@@ -37,6 +37,10 @@
+
+
+
+
desktop:/
diff --git a/containments/desktop/package/contents/ui/FolderView.qml b/containments/desktop/package/contents/ui/FolderView.qml
--- a/containments/desktop/package/contents/ui/FolderView.qml
+++ b/containments/desktop/package/contents/ui/FolderView.qml
@@ -1072,6 +1072,8 @@
parseDesktopFiles: (plasmoid.configuration.url == "desktop:/")
previews: plasmoid.configuration.previews
previewPlugins: plasmoid.configuration.previewPlugins
+ screenMapper: Folder.ScreenMapper
+ appletInterface: plasmoid
onListingCompleted: {
if (!gridView.model && plasmoid.expanded) {
diff --git a/containments/desktop/package/contents/ui/FolderViewLayer.qml b/containments/desktop/package/contents/ui/FolderViewLayer.qml
--- a/containments/desktop/package/contents/ui/FolderViewLayer.qml
+++ b/containments/desktop/package/contents/ui/FolderViewLayer.qml
@@ -188,6 +188,18 @@
onPositionsChanged: {
folderView.positions = plasmoid.configuration.positions;
}
+
+ onScreenMappingChanged: {
+ Folder.ScreenMapper.screenMapping = plasmoid.configuration.screenMapping;
+ }
+ }
+
+ Connections {
+ target: Folder.ScreenMapper
+
+ onScreenMappingChanged: {
+ plasmoid.configuration.screenMapping = Folder.ScreenMapper.screenMapping;
+ }
}
PlasmaCore.ColorScope {
@@ -227,6 +239,7 @@
}
Component.onCompleted: {
+ Folder.ScreenMapper.screenMapping = plasmoid.configuration.screenMapping;
folderView.sortMode = plasmoid.configuration.sortMode;
folderView.positions = plasmoid.configuration.positions;
}
diff --git a/containments/desktop/plugins/folder/CMakeLists.txt b/containments/desktop/plugins/folder/CMakeLists.txt
--- a/containments/desktop/plugins/folder/CMakeLists.txt
+++ b/containments/desktop/plugins/folder/CMakeLists.txt
@@ -20,6 +20,7 @@
viewpropertiesmenu.cpp
wheelinterceptor.cpp
shortcut.cpp
+ screenmapper.cpp
)
install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kde/private/desktopcontainment/folder)
diff --git a/containments/desktop/plugins/folder/autotests/CMakeLists.txt b/containments/desktop/plugins/folder/autotests/CMakeLists.txt
--- a/containments/desktop/plugins/folder/autotests/CMakeLists.txt
+++ b/containments/desktop/plugins/folder/autotests/CMakeLists.txt
@@ -5,6 +5,7 @@
include_directories("..";${include_directories})
ecm_add_tests(
+ screenmappertest.cpp
foldermodeltest.cpp
positionertest.cpp
viewpropertiesmenutest.cpp
diff --git a/containments/desktop/plugins/folder/autotests/foldermodeltest.h b/containments/desktop/plugins/folder/autotests/foldermodeltest.h
--- a/containments/desktop/plugins/folder/autotests/foldermodeltest.h
+++ b/containments/desktop/plugins/folder/autotests/foldermodeltest.h
@@ -32,9 +32,6 @@
Q_OBJECT
private Q_SLOTS:
- void initTestCase();
- void cleanupTestCase();
-
void init();
void cleanup();
void tst_listing();
@@ -48,8 +45,13 @@
void tst_defaultValues();
void tst_actionMenu();
void tst_lockedChanged();
+ void tst_multiScreen();
+ void tst_multiScreenDifferenPath();
+
private:
+ void createTestFolder(const QString &path);
+
FolderModel *m_folderModel;
QTemporaryDir *m_folderDir;
};
diff --git a/containments/desktop/plugins/folder/autotests/foldermodeltest.cpp b/containments/desktop/plugins/folder/autotests/foldermodeltest.cpp
--- a/containments/desktop/plugins/folder/autotests/foldermodeltest.cpp
+++ b/containments/desktop/plugins/folder/autotests/foldermodeltest.cpp
@@ -21,6 +21,7 @@
#include "foldermodeltest.h"
#include "foldermodel.h"
+#include "screenmapper.h"
#include
#include
@@ -30,38 +31,34 @@
static const QLatin1String desktop(QLatin1String("Desktop"));
-void FolderModelTest::initTestCase()
+void FolderModelTest::createTestFolder(const QString &path)
{
- m_folderDir = new QTemporaryDir();
-
QDir dir(m_folderDir->path());
- dir.mkdir(desktop);
- dir.cd(desktop);
+ dir.mkdir(path);
+ dir.cd(path);
dir.mkdir("firstDir");
QFile f;
for (int i = 1; i < 10; i++) {
f.setFileName(QStringLiteral("%1/file%2.txt").arg(dir.path(), QString::number(i)));
f.open(QFile::WriteOnly);
f.close();
}
-
-}
-
-void FolderModelTest::cleanupTestCase()
-{
- delete m_folderDir;
}
void FolderModelTest::init()
{
+ m_folderDir = new QTemporaryDir();
+ createTestFolder(desktop);
m_folderModel = new FolderModel(this);
m_folderModel->setUrl(m_folderDir->path() + QDir::separator() + desktop );
QSignalSpy s(m_folderModel, &FolderModel::listingCompleted);
s.wait(1000);
}
void FolderModelTest::cleanup()
{
+ delete m_folderDir;
+ m_folderDir = 0;
delete m_folderModel;
m_folderModel = nullptr;
}
@@ -265,3 +262,110 @@
m_folderModel->setLocked(true);
QCOMPARE(s.count(), 2);
}
+
+void FolderModelTest::tst_multiScreen()
+{
+ auto *screenMapper = ScreenMapper::instance();
+ m_folderModel->setUsedByContainment(true);
+ m_folderModel->setScreenMapper(screenMapper);
+ m_folderModel->setScreen(0);
+ QSignalSpy s(m_folderModel, &FolderModel::listingCompleted);
+ s.wait(1000);
+ const auto count = m_folderModel->rowCount();
+ for (int i = 0; i < count; i++) {
+ const auto index = m_folderModel->index(i, 0);
+ const auto name = index.data(FolderModel::UrlRole).toString();
+ // all items are on the first screen by default
+ QCOMPARE(screenMapper->screenForItem(name), 0);
+ }
+
+ // move one file to a new screen
+ const auto movedItem = m_folderModel->index(0, 0).data(FolderModel::UrlRole).toString();
+ FolderModel secondFolderModel;
+ secondFolderModel.setUrl(m_folderDir->path() + QDir::separator() + desktop );
+ secondFolderModel.setUsedByContainment(true);
+ secondFolderModel.setScreenMapper(screenMapper);
+ secondFolderModel.setScreen(1);
+ QSignalSpy s2(&secondFolderModel, &FolderModel::listingCompleted);
+ s2.wait(1000);
+ const auto count2 = secondFolderModel.rowCount();
+ QCOMPARE(count2, 0);
+
+ screenMapper->addMapping(movedItem, 1);
+ m_folderModel->invalidate();
+ secondFolderModel.invalidate();
+ s.wait(1000);
+ s2.wait(1000);
+ // we have one less item
+ QCOMPARE(m_folderModel->rowCount(), count - 1);
+ QCOMPARE(secondFolderModel.rowCount(), 1);
+ QCOMPARE(secondFolderModel.index(0,0).data(FolderModel::UrlRole).toString(), movedItem);
+ QCOMPARE(screenMapper->screenForItem(movedItem), 1);
+
+ // remove extra screen, we have all items back
+ screenMapper->removeScreen(1, m_folderModel->url());
+ s.wait(500);
+ QCOMPARE(m_folderModel->rowCount(), count);
+ QCOMPARE(secondFolderModel.rowCount(), 0);
+ QCOMPARE(screenMapper->screenForItem(movedItem), 0);
+
+ // add back extra screen, the item is moved there
+ screenMapper->addScreen(1, m_folderModel->url());
+ s.wait(500);
+ s2.wait(500);
+ QCOMPARE(m_folderModel->rowCount(), count - 1);
+ QCOMPARE(secondFolderModel.rowCount(), 1);
+ QCOMPARE(secondFolderModel.index(0,0).data(FolderModel::UrlRole).toString(), movedItem);
+ QCOMPARE(screenMapper->screenForItem(movedItem), 1);
+
+ // create a new item, it appears on the first screen
+ QDir dir(m_folderDir->path());
+ dir.cd(desktop);
+ dir.mkdir("secondDir");
+ dir.cd("secondDir");
+ s.wait(1000);
+ QCOMPARE(m_folderModel->rowCount(), count);
+ QCOMPARE(secondFolderModel.rowCount(), 1);
+ QCOMPARE(screenMapper->screenForItem("file://" + dir.path()), 0);
+}
+
+void FolderModelTest::tst_multiScreenDifferenPath()
+{
+ auto *screenMapper = ScreenMapper::instance();
+ m_folderModel->setUsedByContainment(true);
+ m_folderModel->setScreenMapper(screenMapper);
+ m_folderModel->setScreen(0);
+ QSignalSpy s(m_folderModel, &FolderModel::listingCompleted);
+ s.wait(1000);
+ const auto count = m_folderModel->rowCount();
+ QCOMPARE(count, 10);
+
+ const QLatin1String desktop2(QLatin1String("Desktop2"));
+ createTestFolder(desktop2);
+ FolderModel secondFolderModel;
+ secondFolderModel.setUsedByContainment(true);
+ secondFolderModel.setScreenMapper(screenMapper);
+ secondFolderModel.setUrl(m_folderDir->path() + QDir::separator() + desktop2 );
+ secondFolderModel.setScreen(1);
+ QSignalSpy s2(&secondFolderModel, &FolderModel::listingCompleted);
+ s2.wait(1000);
+ const auto count2 = secondFolderModel.rowCount();
+ QCOMPARE(count2, 10);
+
+ // create a new item, it appears on the first screen
+ QDir dir(m_folderDir->path());
+ dir.cd(desktop);
+ dir.mkdir("secondDir");
+ s.wait(1000);
+ QCOMPARE(m_folderModel->rowCount(), count + 1);
+ QCOMPARE(secondFolderModel.rowCount(), count2);
+
+
+ // create a new item, it appears on the second screen
+ dir.cd(m_folderDir->path() + QDir::separator() + desktop2);
+ dir.mkdir("secondDir2");
+ s.wait(1000);
+ QCOMPARE(m_folderModel->rowCount(), count + 1);
+ QCOMPARE(secondFolderModel.rowCount(), count2 + 1);
+}
+
diff --git a/containments/desktop/plugins/folder/autotests/foldermodeltest.h b/containments/desktop/plugins/folder/autotests/screenmappertest.h
copy from containments/desktop/plugins/folder/autotests/foldermodeltest.h
copy to containments/desktop/plugins/folder/autotests/screenmappertest.h
--- a/containments/desktop/plugins/folder/autotests/foldermodeltest.h
+++ b/containments/desktop/plugins/folder/autotests/screenmappertest.h
@@ -2,6 +2,7 @@
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company *
* *
* Author: Andras Mantia *
+ * Work sponsored by the LiMux project of the city of Munich. *
* *
* 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 *
@@ -19,39 +20,31 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
-#ifndef FOLDERMODELTEST_H
-#define FOLDERMODELTEST_H
+#ifndef SCREENMAPPERTEST_H
+#define SCREENMAPPERTEST_H
#include
-class QTemporaryDir;
-class FolderModel;
+class ScreenMapper;
-class FolderModelTest : public QObject
+class ScreenMapperTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
- void cleanupTestCase();
-
void init();
- void cleanup();
- void tst_listing();
- void tst_listingDescending();
- void tst_listingFolderNotFirst();
- void tst_filterListing();
- void tst_cd();
- void tst_rename_data();
- void tst_rename();
- void tst_selection();
- void tst_defaultValues();
- void tst_actionMenu();
- void tst_lockedChanged();
-
-private:
- FolderModel *m_folderModel;
- QTemporaryDir *m_folderDir;
+
+ void tst_addScreens();
+ void tst_removeScreens();
+ void tst_addMapping();
+ void tst_addRemoveScreenWithItems();
+ void tst_addRemoveScreenDifferentPaths();
+
+private:
+ void addScreens(const QString &path);
+
+ ScreenMapper *m_screenMapper;
};
-#endif // FOLDERMODELTEST_H
+#endif // SCREENMAPPERTEST_H
diff --git a/containments/desktop/plugins/folder/autotests/screenmappertest.cpp b/containments/desktop/plugins/folder/autotests/screenmappertest.cpp
new file mode 100644
--- /dev/null
+++ b/containments/desktop/plugins/folder/autotests/screenmappertest.cpp
@@ -0,0 +1,159 @@
+/***************************************************************************
+ * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company *
+ * *
+ * Author: Andras Mantia *
+ * Work sponsored by the LiMux project of the city of Munich. *
+ * *
+ * 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 "screenmappertest.h"
+#include "screenmapper.h"
+
+#include
+#include
+
+QTEST_MAIN(ScreenMapperTest)
+
+void ScreenMapperTest::initTestCase()
+{
+ m_screenMapper = ScreenMapper::instance();
+}
+
+void ScreenMapperTest::init()
+{
+ m_screenMapper->cleanup();
+}
+
+void ScreenMapperTest::tst_addScreens()
+{
+ const auto path = QStringLiteral("desktop:/");
+ QSignalSpy s(m_screenMapper, &ScreenMapper::screensChanged);
+ m_screenMapper->addScreen(-1, path);
+ QCOMPARE(s.count(), 0);
+ m_screenMapper->addScreen(1, path);
+ QCOMPARE(s.count(), 1);
+ m_screenMapper->addScreen(0, path);
+ QCOMPARE(s.count(), 2);
+ m_screenMapper->addScreen(1, path);
+ QCOMPARE(s.count(), 2);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 0);
+}
+
+void ScreenMapperTest::tst_removeScreens()
+{
+ const auto path = QStringLiteral("desktop:/");
+ addScreens(path);
+ QSignalSpy s(m_screenMapper, &ScreenMapper::screensChanged);
+ m_screenMapper->removeScreen(-1, path);
+ QCOMPARE(s.count(), 0);
+ m_screenMapper->removeScreen(1, path);
+ QCOMPARE(s.count(), 1);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 0);
+ m_screenMapper->removeScreen(1, path);
+ QCOMPARE(s.count(), 1);
+ m_screenMapper->addScreen(3, path);
+ QCOMPARE(s.count(), 2);
+ m_screenMapper->removeScreen(0, path);
+ QCOMPARE(s.count(), 3);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 2);
+}
+
+void ScreenMapperTest::tst_addMapping()
+{
+ const auto path = QStringLiteral("desktop:/");
+ addScreens(path);
+ QSignalSpy s(m_screenMapper, &ScreenMapper::screenMappingChanged);
+ QString file("desktop:/foo%1.txt");
+
+ for (int i = 0 ; i < 3; i++) {
+ const QString name = file.arg(i);
+ m_screenMapper->addMapping(name, i);
+ QCOMPARE(s.count(), i + 1);
+ QCOMPARE(m_screenMapper->screenForItem(name), i);
+ }
+}
+
+void ScreenMapperTest::tst_addRemoveScreenWithItems()
+{
+ const auto path = QStringLiteral("desktop:/");
+ addScreens(path);
+ QString file("desktop:/foo%1.txt");
+
+ for (int i = 0 ; i < 3; i++) {
+ const QString name = file.arg(i);
+ m_screenMapper->addMapping(name, i);
+ }
+
+ // remove one screen
+ m_screenMapper->removeScreen(1, path);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(0)), 0);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(1)), -1);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(2)), 2);
+
+ // add removed screen back, items screen is restored
+ m_screenMapper->addScreen(1, path);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(0)), 0);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(1)), 1);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(2)), 2);
+
+ // remove all screens, firstAvailableScreen changes
+ m_screenMapper->removeScreen(0, path);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 1);
+ m_screenMapper->removeScreen(1, path);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 2);
+ m_screenMapper->removeScreen(2, path);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), -1);
+
+
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(0)), -1);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(1)), -1);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(2)), -1);
+
+ // add all screens back, all item's screen is restored
+ addScreens(path);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(0)), 0);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(1)), 1);
+ QCOMPARE(m_screenMapper->screenForItem(file.arg(2)), 2);
+
+ // remove one screen and move its item
+ const QString movedItem = file.arg(1);
+ m_screenMapper->removeScreen(1, path);
+ QCOMPARE(m_screenMapper->screenForItem(movedItem), -1);
+ m_screenMapper->addMapping(movedItem, 0);
+ QCOMPARE(m_screenMapper->screenForItem(movedItem), 0);
+
+ // add back the screen, item goes back to the original place
+ m_screenMapper->addScreen(1, path);
+ QCOMPARE(m_screenMapper->screenForItem(movedItem), 1);
+}
+
+void ScreenMapperTest::tst_addRemoveScreenDifferentPaths()
+{
+ const auto path = QStringLiteral("desktop:/Foo");
+ const auto path2 = QStringLiteral("desktop:/Foo2");
+ m_screenMapper->addScreen(0, path);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path), 0);
+ QCOMPARE(m_screenMapper->firstAvailableScreen(path2), -1);
+
+}
+
+void ScreenMapperTest::addScreens(const QString &path)
+{
+ m_screenMapper->addScreen(0, path);
+ m_screenMapper->addScreen(1, path);
+ m_screenMapper->addScreen(2, path);
+}
diff --git a/containments/desktop/plugins/folder/foldermodel.h b/containments/desktop/plugins/folder/foldermodel.h
--- a/containments/desktop/plugins/folder/foldermodel.h
+++ b/containments/desktop/plugins/folder/foldermodel.h
@@ -56,6 +56,8 @@
class DropJob;
}
+class ScreenMapper;
+
class DirLister : public KDirLister
{
Q_OBJECT
@@ -94,6 +96,8 @@
Q_PROPERTY(QString filterPattern READ filterPattern WRITE setFilterPattern NOTIFY filterPatternChanged)
Q_PROPERTY(QStringList filterMimeTypes READ filterMimeTypes WRITE setFilterMimeTypes NOTIFY filterMimeTypesChanged)
Q_PROPERTY(QObject* newMenu READ newMenu CONSTANT)
+ Q_PROPERTY(ScreenMapper* screenMapper READ screenMapper WRITE setScreenMapper NOTIFY screenMapperChanged)
+ Q_PROPERTY(QObject* appletInterface READ appletInterface WRITE setAppletInterface NOTIFY appletInterfaceChanged);
public:
enum DataRole {
@@ -180,6 +184,12 @@
QStringList filterMimeTypes() const;
void setFilterMimeTypes(const QStringList &mimeList);
+ ScreenMapper* screenMapper() const;
+ void setScreenMapper(ScreenMapper* screenMapper);
+
+ QObject *appletInterface() const;
+ void setAppletInterface(QObject *appletInterface);
+
KFileItem rootItem() const;
Q_INVOKABLE void up();
@@ -233,6 +243,8 @@
Q_INVOKABLE void undo();
Q_INVOKABLE void refresh();
+ void setScreen(int screen);
+
Q_SIGNALS:
void urlChanged() const;
void listingCompleted() const;
@@ -254,6 +266,9 @@
void filterModeChanged() const;
void filterPatternChanged() const;
void filterMimeTypesChanged() const;
+ void screenChanged() const;
+ void screenMapperChanged() const;
+ void appletInterfaceChanged() const;
void requestRename() const;
void move(int x, int y, QList urls);
void popupMenuAboutToShow(KIO::DropJob *dropJob, QMimeData *mimeData, int x, int y);
@@ -321,6 +336,9 @@
bool m_filterPatternMatchAll;
QSet m_mimeSet;
QList m_regExps;
+ int m_screen = -1;
+ ScreenMapper *m_screenMapper = nullptr;
+ QObject *m_appletInterface = nullptr;
};
#endif
diff --git a/containments/desktop/plugins/folder/foldermodel.cpp b/containments/desktop/plugins/folder/foldermodel.cpp
--- a/containments/desktop/plugins/folder/foldermodel.cpp
+++ b/containments/desktop/plugins/folder/foldermodel.cpp
@@ -24,6 +24,7 @@
#include "foldermodel.h"
#include "itemviewadapter.h"
#include "positioner.h"
+#include "screenmapper.h"
#include
#include
@@ -70,6 +71,10 @@
#include
#include
+#include
+#include
+#include
+
#include
#include
#include
@@ -154,6 +159,12 @@
FolderModel::~FolderModel()
{
+ if (m_screenMapper) {
+ // disconnect so we don't handle signals from the screen mapper when
+ // removeScreen is called
+ m_screenMapper->disconnect(this);
+ m_screenMapper->removeScreen(m_screen, url());
+ }
}
QHash< int, QByteArray > FolderModel::roleNames() const
@@ -194,6 +205,8 @@
return;
}
+ const auto oldUrl = m_url;
+
beginResetModel();
m_url = url;
m_isDirCache.clear();
@@ -225,6 +238,11 @@
}
emit iconNameChanged();
+
+ if (m_screenMapper) {
+ m_screenMapper->removeScreen(m_screen, oldUrl);
+ m_screenMapper->addScreen(m_screen, url);
+ }
}
QUrl FolderModel::resolvedUrl() const
@@ -518,6 +536,18 @@
}
}
+void FolderModel::setScreen(int screen)
+{
+ if (m_screen == screen)
+ return;
+
+ m_screen = screen;
+ if (m_usedByContainment && m_screenMapper) {
+ m_screenMapper->addScreen(screen, url());
+ }
+ emit screenChanged();
+}
+
KFileItem FolderModel::rootItem() const
{
return m_dirModel->dirLister()->rootItem();
@@ -1162,6 +1192,9 @@
void FolderModel::evictFromIsDirCache(const KFileItemList& items)
{
foreach (const KFileItem &item, items) {
+ if (m_screenMapper) {
+ m_screenMapper->removeFromMap(item.url().toString());
+ }
m_isDirCache.remove(item.url());
}
}
@@ -1283,13 +1316,34 @@
bool FolderModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
+ const KDirModel *dirModel = static_cast(sourceModel());
+ const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent));
+
+ if (m_usedByContainment && m_screenMapper) {
+ const QString name = item.url().toString();
+ const int screen = m_screenMapper->screenForItem(name);
+ // don't do anything if the folderview is not associated with a screen
+ if (m_screen != -1) {
+ if (screen == -1) {
+ // The item is not associated with a screen, probably because this is the first
+ // time we see it or the folderview was previously used as a regular applet.
+ // Associated with this folderview if the view is on the first available screen
+ if (m_screen == m_screenMapper->firstAvailableScreen(url())) {
+ m_screenMapper->addMapping(name, m_screen, ScreenMapper::DelayedSignal);
+ } else {
+ return false;
+ }
+ } else if (m_screen != screen) {
+ // the item belongs to a different screen, filter it out
+ return false;
+ }
+ }
+ }
+
if (m_filterMode == NoFilter) {
return true;
}
- const KDirModel *dirModel = static_cast(sourceModel());
- const KFileItem item = dirModel->itemForIndex(dirModel->index(sourceRow, KDirModel::Name, sourceParent));
-
if (m_filterMode == FilterShowMatches) {
return (matchPattern(item) && matchMimeType(item));
} else {
@@ -1617,6 +1671,66 @@
m_dirModel->dirLister()->updateDirectory(m_dirModel->dirLister()->url());
}
+ScreenMapper *FolderModel::screenMapper() const
+{
+ return m_screenMapper;
+}
+
+void FolderModel::setScreenMapper(ScreenMapper *screenMapper)
+{
+ if (m_screenMapper == screenMapper)
+ return;
+
+ Q_ASSERT(!m_screenMapper);
+
+ if (m_screenMapper) {
+ m_screenMapper->disconnect(this);
+ }
+
+ m_screenMapper = screenMapper;
+ if (m_screenMapper) {
+ connect(m_screenMapper, &ScreenMapper::screensChanged, this, &FolderModel::invalidateFilter);
+ connect(m_screenMapper, &ScreenMapper::screenMappingChanged, this, &FolderModel::invalidateFilter);
+ }
+
+ invalidateFilter();
+ emit screenMapperChanged();
+}
+
+QObject *FolderModel::appletInterface() const
+{
+ return m_appletInterface;
+}
+
+void FolderModel::setAppletInterface(QObject *appletInterface)
+{
+ if (m_appletInterface != appletInterface) {
+ Q_ASSERT(!m_appletInterface);
+
+ m_appletInterface = appletInterface;
+
+ if (appletInterface) {
+ Plasma::Applet *applet = appletInterface->property("_plasma_applet").value();
+
+ if (applet) {
+ Plasma::Containment *containment = applet->containment();
+
+ if (containment) {
+ Plasma::Corona *corona = containment->corona();
+
+ if (corona) {
+ m_screenMapper->setCorona(corona);
+ }
+ setScreen(containment->screen());
+ connect(containment, &Plasma::Containment::screenChanged, this, &FolderModel::setScreen);
+ }
+ }
+ }
+
+ emit appletInterfaceChanged();
+ }
+}
+
void FolderModel::moveSelectedToTrash()
{
if (!m_selectionModel->hasSelection()) {
diff --git a/containments/desktop/plugins/folder/folderplugin.cpp b/containments/desktop/plugins/folder/folderplugin.cpp
--- a/containments/desktop/plugins/folder/folderplugin.cpp
+++ b/containments/desktop/plugins/folder/folderplugin.cpp
@@ -32,15 +32,28 @@
#include "viewpropertiesmenu.h"
#include "wheelinterceptor.h"
#include "shortcut.h"
+#include "screenmapper.h"
+#include
+#include
static QObject *menuHelperSingletonProvider(QQmlEngine *engine, QJSEngine *jsEngine)
{
Q_UNUSED(engine);
Q_UNUSED(jsEngine);
return new MenuHelper();
}
+static QObject *screenMapperProvider(QQmlEngine *engine, QJSEngine *scriptEngine)
+{
+ Q_UNUSED(scriptEngine);
+
+ QObject *mapper = ScreenMapper::instance();
+ engine->setObjectOwnership(mapper, QQmlEngine::CppOwnership);
+ return mapper;
+}
+
+
void FolderPlugin::registerTypes(const char *uri)
{
Q_ASSERT(uri == QLatin1String("org.kde.private.desktopcontainment.folder"));
@@ -58,5 +71,6 @@
qmlRegisterType(uri, 0, 1, "ViewPropertiesMenu");
qmlRegisterType(uri, 0, 1, "WheelInterceptor");
qmlRegisterType(uri, 0, 1, "ShortCut");
+ qmlRegisterSingletonType(uri, 0, 1, "ScreenMapper", screenMapperProvider);
}
diff --git a/containments/desktop/plugins/folder/screenmapper.h b/containments/desktop/plugins/folder/screenmapper.h
new file mode 100644
--- /dev/null
+++ b/containments/desktop/plugins/folder/screenmapper.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company *
+ * *
+ * Author: Andras Mantia *
+ * Work sponsored by the LiMux project of the city of Munich. *
+ * *
+ * 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 SCREENMAPPER_H
+#define SCREENMAPPER_H
+
+#include
+#include
+#include
+
+#include "folderplugin_private_export.h"
+
+class QTimer;
+
+namespace Plasma {
+ class Corona;
+}
+
+class FOLDERPLUGIN_TESTS_EXPORT ScreenMapper : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringList screenMapping READ screenMapping WRITE setScreenMapping NOTIFY screenMappingChanged)
+
+public:
+ enum MappingSignalBehavior {
+ DelayedSignal = 0,
+ ImmediateSignal
+ };
+
+ static ScreenMapper *instance();
+ ~ScreenMapper() override = default;
+
+ QStringList screenMapping() const;
+ void setScreenMapping(const QStringList &mapping);
+
+ int screenForItem(const QString &name) const;
+ void addMapping(const QString &name, int screen, MappingSignalBehavior behavior = ImmediateSignal);
+ void removeFromMap(const QString &name);
+ void setCorona(Plasma::Corona *corona);
+
+ void addScreen(int screenId, const QString &path);
+ void removeScreen(int screenId, const QString &path);
+ int firstAvailableScreen(const QString &path) const;
+
+#ifdef BUILD_TESTING
+ void cleanup();
+#endif
+
+Q_SIGNALS:
+ void screenMappingChanged() const;
+ void screensChanged() const;
+
+private:
+ ScreenMapper(QObject *parent = nullptr);
+
+ QHash m_screenItemMap;
+ QHash m_itemsOnDisabledScreensMap;
+ QHash m_firstScreenForPath; // first available screen for a path
+ QHash m_screensPerPath; // screen per registered path
+ QVector m_availableScreens;
+ Plasma::Corona *m_corona = nullptr;
+ QTimer *m_screenMappingChangedTimer = nullptr;
+};
+
+#endif // SCREENMAPPER_H
diff --git a/containments/desktop/plugins/folder/screenmapper.cpp b/containments/desktop/plugins/folder/screenmapper.cpp
new file mode 100644
--- /dev/null
+++ b/containments/desktop/plugins/folder/screenmapper.cpp
@@ -0,0 +1,229 @@
+/***************************************************************************
+ * Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company *
+ * *
+ * Author: Andras Mantia *
+ * Work sponsored by the LiMux project of the city of Munich. *
+ * 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 "screenmapper.h"
+
+#include
+#include
+
+#include
+
+ScreenMapper *ScreenMapper::instance()
+{
+ static ScreenMapper *s_instance = new ScreenMapper();
+ return s_instance;
+}
+
+ScreenMapper::ScreenMapper(QObject *parent)
+ : QObject(parent)
+ , m_screenMappingChangedTimer(new QTimer(this))
+
+{
+ connect(m_screenMappingChangedTimer, &QTimer::timeout,
+ this, &ScreenMapper::screenMappingChanged);
+
+ // used to compress screenMappingChanged signals when addMapping is called multiple times,
+ // eg. from FolderModel::filterAcceptRows. The timer interval is an arbitrary number,
+ // that doesn't delay too much the signal, but still compresses as much as possible
+ m_screenMappingChangedTimer->setInterval(100);
+ m_screenMappingChangedTimer->setSingleShot(true);
+}
+
+void ScreenMapper::removeScreen(int screenId, const QString &path)
+{
+ if (screenId < 0 || !m_availableScreens.contains(screenId))
+ return;
+
+ QUrl screenUrl = QUrl::fromUserInput(path, {}, QUrl::AssumeLocalFile);
+ const auto screenPathWithScheme = screenUrl.url();
+ // store the original location for the items
+ auto it = m_screenItemMap.constBegin();
+ while (it != m_screenItemMap.constEnd()) {
+ const auto name = it.key();
+ if (it.value() == screenId && name.startsWith(screenPathWithScheme)) {
+ m_itemsOnDisabledScreensMap[screenId].append(name);
+ }
+ ++it;
+ }
+
+ m_availableScreens.removeAll(screenId);
+
+ const auto newFirstScreen = std::min_element(m_availableScreens.constBegin(), m_availableScreens.constEnd());
+ auto pathIt = m_screensPerPath.find(path);
+ if (pathIt != m_screensPerPath.end() && pathIt.value() > 0) {
+ int firstScreen = m_firstScreenForPath.value(path, -1);
+ if (firstScreen == screenId) {
+ m_firstScreenForPath[path] = (newFirstScreen == m_availableScreens.constEnd()) ? -1 : *newFirstScreen;
+ }
+ *pathIt = pathIt.value() - 1;
+ } else if (path.isEmpty()) {
+ // The screen got completely removed, not only its path changed.
+ // If the removed screen was the first screen for a desktop path, the first screen for that path
+ // needs to be updated.
+ for (auto it = m_firstScreenForPath.begin(); it != m_firstScreenForPath.end(); ++it) {
+ if (*it == screenId) {
+ *it = *newFirstScreen;
+
+ // we have now the path for the screen that was removed, so adjust it
+ pathIt = m_screensPerPath.find(it.key());
+ if (pathIt != m_screensPerPath.end()) {
+ *pathIt = pathIt.value() - 1;
+ }
+ }
+ }
+ }
+
+ emit screensChanged();
+}
+
+void ScreenMapper::addScreen(int screenId, const QString &path)
+{
+ if (screenId < 0 || m_availableScreens.contains(screenId))
+ return;
+
+ QUrl screenUrl = QUrl::fromUserInput(path, {}, QUrl::AssumeLocalFile);
+ const auto screenPathWithScheme = screenUrl.url();
+ const bool isEmpty = (path.isEmpty() || screenUrl.path() == "/");
+ // restore the stored locations
+ auto it = m_itemsOnDisabledScreensMap.find(screenId);
+ if (it != m_itemsOnDisabledScreensMap.end()) {
+ auto items = it.value();
+ for (const auto &name: it.value()) {
+ // add the items to the new screen, if they are on a disabled screen and their
+ // location is below the new screen's path
+ if (isEmpty || name.startsWith(screenPathWithScheme)) {
+ addMapping(name, screenId, DelayedSignal);
+ items.removeAll(name);
+ }
+ }
+ if (items.isEmpty()) {
+ m_itemsOnDisabledScreensMap.erase(it);
+ } else {
+ *it = items;
+ }
+ }
+
+ m_availableScreens.append(screenId);
+
+ // path is empty when a new screen appears that has no folderview base path associated with
+ if (!path.isEmpty()) {
+ auto it = m_screensPerPath.find(path);
+ int firstScreen = m_firstScreenForPath.value(path, -1);
+ if (firstScreen == -1 || screenId < firstScreen) {
+ m_firstScreenForPath[path] = screenId;
+ }
+ if (it == m_screensPerPath.end()) {
+ m_screensPerPath[path] = 1;
+ } else {
+ *it = it.value() + 1;
+ }
+ }
+
+ emit screensChanged();
+}
+
+void ScreenMapper::addMapping(const QString &name, int screen, MappingSignalBehavior behavior)
+{
+ m_screenItemMap[name] = screen;
+ if (behavior == DelayedSignal) {
+ m_screenMappingChangedTimer->start();
+ } else {
+ emit screenMappingChanged();
+ }
+}
+
+void ScreenMapper::removeFromMap(const QString &name)
+{
+ m_screenItemMap.remove(name);
+ m_screenMappingChangedTimer->start();
+}
+
+int ScreenMapper::firstAvailableScreen(const QString &path) const
+{
+ return m_firstScreenForPath.value(path, -1);
+}
+
+#ifdef BUILD_TESTING
+void ScreenMapper::cleanup()
+{
+ m_screenItemMap.clear();
+ m_itemsOnDisabledScreensMap.clear();
+ m_firstScreenForPath.clear();
+ m_screensPerPath.clear();
+ m_availableScreens.clear();
+}
+#endif
+
+void ScreenMapper::setCorona(Plasma::Corona *corona)
+{
+ if (m_corona != corona) {
+ Q_ASSERT(!m_corona);
+
+ m_corona = corona;
+ if (m_corona) {
+ connect(m_corona, &Plasma::Corona::screenRemoved, this, [this] (int screenId) {
+ removeScreen(screenId, {});
+ });
+ connect(m_corona, &Plasma::Corona::screenAdded, this, [this] (int screenId) {
+ addScreen(screenId, {});
+ });
+ }
+ }
+}
+
+QStringList ScreenMapper::screenMapping() const
+{
+ QStringList result;
+ result.reserve(m_screenItemMap.count() * 2);
+ auto it = m_screenItemMap.constBegin();
+ while (it != m_screenItemMap.constEnd()) {
+ result.append(it.key());
+ result.append(QString::number(it.value()));
+ ++it;
+ }
+
+ return result;
+}
+
+void ScreenMapper::setScreenMapping(const QStringList &mapping)
+{
+ QHash newMap;
+ const int count = mapping.count();
+ newMap.reserve(count / 2);
+ for (int i = 0; i < count - 1; i += 2) {
+ if (i + 1 < count) {
+ newMap[mapping[i]] = mapping[i + 1].toInt();
+ }
+ }
+
+ if (m_screenItemMap != newMap) {
+ m_screenItemMap = newMap;
+ emit screenMappingChanged();
+ }
+}
+
+int ScreenMapper::screenForItem(const QString &name) const
+{
+ int screen = m_screenItemMap.value(name, -1);
+ if (!m_availableScreens.contains(screen))
+ screen = -1;
+
+ return screen;
+}