diff --git a/autotests/kdiroperatortest.cpp b/autotests/kdiroperatortest.cpp index 0178eef0..44044503 100644 --- a/autotests/kdiroperatortest.cpp +++ b/autotests/kdiroperatortest.cpp @@ -1,139 +1,140 @@ /* This file is part of the KDE libraries Copyright (c) 2009 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 Lesser 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 #include #include #include #include #include #include /** * Unit test for KDirOperator */ class KDirOperatorTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase() { // To avoid a runtime dependency on klauncher qputenv("KDE_FORK_SLAVES", "yes"); } void cleanupTestCase() { } void testNoViewConfig() { KDirOperator dirOp; // setIconsZoom tries to write config. // Make sure it won't crash if setViewConfig() isn't called dirOp.setIconsZoom(5); QCOMPARE(dirOp.iconsZoom(), 5); } void testReadConfig() { // Test: Make sure readConfig() and then setView() restores // the correct kind of view. KDirOperator *dirOp = new KDirOperator; dirOp->setView(KFile::DetailTree); dirOp->setShowHiddenFiles(true); KConfigGroup cg(KSharedConfig::openConfig(), "diroperator"); dirOp->writeConfig(cg); delete dirOp; dirOp = new KDirOperator; dirOp->readConfig(cg); dirOp->setView(KFile::Default); QVERIFY(dirOp->showHiddenFiles()); // KDirOperatorDetail inherits QTreeView, so this test should work QVERIFY(qobject_cast(dirOp->view())); delete dirOp; } /** * testBug187066 does the following: * * 1. Open a KDirOperator in kdelibs/kfile * 2. Set the current item to "file:///" * 3. Set the current item to "file:///.../kdelibs/kfile/tests/kdiroperatortest.cpp" * * This may result in a crash, see https://bugs.kde.org/show_bug.cgi?id=187066 */ void testBug187066() { const QString dir = QFileInfo(QFINDTESTDATA("kdiroperatortest.cpp")).absolutePath(); const QUrl kFileDirUrl(QUrl::fromLocalFile(dir).adjusted(QUrl::RemoveFilename)); KDirOperator dirOp(kFileDirUrl); QSignalSpy completedSpy(dirOp.dirLister(), SIGNAL(completed())); dirOp.setView(KFile::DetailTree); completedSpy.wait(1000); dirOp.setCurrentItem(QUrl(QStringLiteral("file:///"))); dirOp.setCurrentItem(QUrl::fromLocalFile(QFINDTESTDATA("kdiroperatortest.cpp"))); //completedSpy.wait(1000); QTest::qWait(1000); } void testSetUrlPathAdjustment_data() { QTest::addColumn("url"); QTest::addColumn("expectedUrl"); QTest::newRow("with_host") << QUrl(QStringLiteral("ftp://foo.com/folder")) << QUrl(QStringLiteral("ftp://foo.com/folder/")); QTest::newRow("with_no_host") << QUrl(QStringLiteral("smb://")) << QUrl(QStringLiteral("smb://")); QTest::newRow("with_host_without_path") << QUrl(QStringLiteral("ftp://user@example.com")) << QUrl(QStringLiteral("ftp://user@example.com")); + QTest::newRow("with_trailing_slashs") << QUrl::fromLocalFile(QDir::tempPath() + QLatin1String("////////")) << QUrl::fromLocalFile(QDir::tempPath() + QLatin1Char('/')); } void testSetUrlPathAdjustment() { QFETCH(QUrl, url); QFETCH(QUrl, expectedUrl); KDirOperator dirOp; QSignalSpy spy(&dirOp, SIGNAL(urlEntered(QUrl))); dirOp.setUrl(url, true); QCOMPARE(spy.takeFirst().at(0).toUrl(), expectedUrl); } void testSupportedSchemes() { KDirOperator dirOp; QSignalSpy spy(&dirOp, &KDirOperator::urlEntered); QCOMPARE(dirOp.supportedSchemes(), QStringList()); dirOp.setSupportedSchemes({"file"}); QCOMPARE(dirOp.supportedSchemes(), QStringList("file")); dirOp.setUrl(QUrl("smb://foo/bar"), true); QCOMPARE(spy.count(), 0); const auto fileUrl = QUrl::fromLocalFile(QDir::homePath() + QLatin1Char('/')); dirOp.setUrl(fileUrl, true); QCOMPARE(spy.count(), 1); QCOMPARE(spy.first().at(0).toUrl(), fileUrl); } }; QTEST_MAIN(KDirOperatorTest) #include "kdiroperatortest.moc" diff --git a/autotests/kurifiltertest.cpp b/autotests/kurifiltertest.cpp index d030742e..08c7b8de 100644 --- a/autotests/kurifiltertest.cpp +++ b/autotests/kurifiltertest.cpp @@ -1,479 +1,484 @@ /* * Copyright (C) 2002, 2003 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * 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 "kurifiltertest.h" #include #include #include #include #include #include #include #include #include QTEST_MAIN(KUriFilterTest) static const char *const s_uritypes[] = { "NetProtocol", "LOCAL_FILE", "LOCAL_DIR", "EXECUTABLE", "HELP", "SHELL", "BLOCKED", "ERROR", "UNKNOWN" }; #define NO_FILTERING -2 static void setupColumns() { QTest::addColumn("input"); QTest::addColumn("expectedResult"); QTest::addColumn("expectedUriType"); QTest::addColumn("list"); QTest::addColumn("absPath"); QTest::addColumn("checkForExecutables"); } static void addRow(const char *input, const QString &expectedResult = QString(), int expectedUriType = -1, const QStringList &list = QStringList(), const QString &absPath = QString(), bool checkForExecutables = true) { QTest::newRow(input) << input << expectedResult << expectedUriType << list << absPath << checkForExecutables; } static void runFilterTest(const QString &a, const QString &expectedResult = nullptr, int expectedUriType = -1, const QStringList &list = QStringList(), const QString &absPath = nullptr, bool checkForExecutables = true) { KUriFilterData *filterData = new KUriFilterData; filterData->setData(a); filterData->setCheckForExecutables(checkForExecutables); if (!absPath.isEmpty()) { filterData->setAbsolutePath(absPath); qDebug() << "Filtering: " << a << " with absPath=" << absPath; } else { qDebug() << "Filtering: " << a; } if (KUriFilter::self()->filterUri(*filterData, list)) { if (expectedUriType == NO_FILTERING) { qCritical() << a << "Did not expect filtering. Got" << filterData->uri(); QVERIFY(expectedUriType != NO_FILTERING); // fail the test } // Copied from minicli... QString cmd; QUrl uri = filterData->uri(); if (uri.isLocalFile() && !uri.hasFragment() && !uri.hasQuery() && (filterData->uriType() != KUriFilterData::NetProtocol)) { cmd = uri.toLocalFile(); } else { cmd = uri.url(QUrl::FullyEncoded); } switch (filterData->uriType()) { case KUriFilterData::LocalFile: case KUriFilterData::LocalDir: qDebug() << "*** Result: Local Resource => '" << filterData->uri().toLocalFile() << "'" << endl; break; case KUriFilterData::Help: qDebug() << "*** Result: Local Resource => '" << filterData->uri().url() << "'" << endl; break; case KUriFilterData::NetProtocol: qDebug() << "*** Result: Network Resource => '" << filterData->uri().url() << "'" << endl; break; case KUriFilterData::Shell: case KUriFilterData::Executable: if (filterData->hasArgsAndOptions()) { cmd += filterData->argsAndOptions(); } qDebug() << "*** Result: Executable/Shell => '" << cmd << "'"; break; case KUriFilterData::Error: qDebug() << "*** Result: Encountered error => '" << cmd << "'"; qDebug() << "Reason:" << filterData->errorMsg(); break; default: qDebug() << "*** Result: Unknown or invalid resource."; } if (!expectedResult.isEmpty()) { // Hack for other locales than english, normalize google hosts to google.com cmd = cmd.replace(QRegExp(QStringLiteral("www\\.google\\.[^/]*/")), QStringLiteral("www.google.com/")); if (cmd != expectedResult) { qWarning() << a; QCOMPARE(cmd, expectedResult); } } if (expectedUriType != -1 && expectedUriType != filterData->uriType()) { qWarning() << a << "Got URI type" << s_uritypes[filterData->uriType()] << "expected" << s_uritypes[expectedUriType]; QCOMPARE(s_uritypes[filterData->uriType()], s_uritypes[expectedUriType]); } } else { if (expectedUriType == NO_FILTERING) { qDebug() << "*** No filtering required."; } else { qDebug() << "*** Could not be filtered."; if (expectedUriType != filterData->uriType()) { QCOMPARE(s_uritypes[filterData->uriType()], s_uritypes[expectedUriType]); } } } delete filterData; qDebug() << "-----"; } static void runFilterTest() { QFETCH(QString, input); QFETCH(QString, expectedResult); QFETCH(int, expectedUriType); QFETCH(QStringList, list); QFETCH(QString, absPath); QFETCH(bool, checkForExecutables); runFilterTest(input, expectedResult, expectedUriType, list, absPath, checkForExecutables); } static void testLocalFile(const QString &filename) { QFile tmpFile(filename); // Yeah, I know, security risk blah blah. This is a test prog! if (tmpFile.open(QIODevice::ReadWrite)) { QByteArray fname = QFile::encodeName(tmpFile.fileName()); runFilterTest(fname, fname, KUriFilterData::LocalFile); tmpFile.close(); tmpFile.remove(); } else { qDebug() << "Couldn't create " << tmpFile.fileName() << ", skipping test"; } } static char s_delimiter = ':'; // the alternative is ' ' void KUriFilterTest::initTestCase() { QStandardPaths::setTestModeEnabled(true); minicliFilters << QStringLiteral("kshorturifilter") << QStringLiteral("kurisearchfilter") << QStringLiteral("localdomainurifilter"); qtdir = qgetenv("QTDIR"); home = qgetenv("HOME"); qputenv("DATAHOME", QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation))); datahome = qgetenv("DATAHOME"); qDebug() << "libpaths" << QCoreApplication::libraryPaths(); qputenv("KDE_FORK_SLAVES", "yes"); // simpler, for the final cleanup QLoggingCategory::setFilterRules(QStringLiteral("org.kde.kurifilter-*=true")); QString searchProvidersDir = QFINDTESTDATA("../src/urifilters/ikws/searchproviders/google.desktop").section('/', 0, -2); QVERIFY(!searchProvidersDir.isEmpty()); qputenv("KIO_SEARCHPROVIDERS_DIR", QFile::encodeName(searchProvidersDir)); // Allow testing of the search engine using both delimiters... const char *envDelimiter = ::getenv("KURIFILTERTEST_DELIMITER"); if (envDelimiter) { s_delimiter = envDelimiter[0]; } // Many tests check the "default search engine" feature. // There is no default search engine by default (since it was annoying when making typos), // so the user has to set it up, which we do here. { KConfigGroup cfg(KSharedConfig::openConfig(QStringLiteral("kuriikwsfilterrc"), KConfig::SimpleConfig), "General"); cfg.writeEntry("DefaultWebShortcut", "google"); cfg.writeEntry("KeywordDelimiter", QString(s_delimiter)); cfg.sync(); } // Copy kshorturifilterrc from the src dir so we don't depend on make install / env vars. { const QString rcFile = QFINDTESTDATA("../src/urifilters/shorturi/kshorturifilterrc"); QVERIFY(!rcFile.isEmpty()); const QString localFile = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/kshorturifilterrc"; QFile::remove(localFile); QVERIFY(QFile(rcFile).copy(localFile)); } QDir().mkpath(datahome + QStringLiteral("/urifilter")); } void KUriFilterTest::pluginNames() { const QStringList plugins = KUriFilter::self()->pluginNames(); qDebug() << plugins; const QByteArray debugString = plugins.join(',').toLatin1(); // To make it possible to have external plugins (if there's any...) // we don't just have an expected result list, we just probe it for specific entries. QVERIFY2(plugins.contains("kshorturifilter"), debugString.constData()); QVERIFY2(plugins.contains("kurisearchfilter"), debugString.constData()); QVERIFY2(plugins.contains("localdomainurifilter"), debugString.constData()); QVERIFY2(plugins.contains("fixhosturifilter"), debugString.constData()); QVERIFY2(plugins.contains("kuriikwsfilter"), debugString.constData()); // No duplicates QCOMPARE(plugins.count("kshorturifilter"), 1); } void KUriFilterTest::noFiltering_data() { setupColumns(); // URI that should require no filtering addRow("http://www.kde.org", QStringLiteral("http://www.kde.org"), KUriFilterData::NetProtocol); - addRow("http://www.kde.org/developer//index.html", QStringLiteral("http://www.kde.org/developer//index.html"), KUriFilterData::NetProtocol); + addRow("http://www.kde.org/developer//index.html", QStringLiteral("http://www.kde.org/developer/index.html"), KUriFilterData::NetProtocol); addRow("file:///", QStringLiteral("/"), KUriFilterData::LocalDir); addRow("file:///etc", QStringLiteral("/etc"), KUriFilterData::LocalDir); addRow("file:///etc/passwd", QStringLiteral("/etc/passwd"), KUriFilterData::LocalFile); } void KUriFilterTest::noFiltering() { runFilterTest(); } void KUriFilterTest::localFiles_data() { setupColumns(); addRow("/", QStringLiteral("/"), KUriFilterData::LocalDir); addRow("/", QStringLiteral("/"), KUriFilterData::LocalDir, QStringList(QStringLiteral("kshorturifilter"))); addRow("//", QStringLiteral("/"), KUriFilterData::LocalDir); addRow("///", QStringLiteral("/"), KUriFilterData::LocalDir); addRow("////", QStringLiteral("/"), KUriFilterData::LocalDir); addRow("///tmp", QStringLiteral("/tmp"), KUriFilterData::LocalDir); + addRow("///tmp/", QStringLiteral("/tmp/"), KUriFilterData::LocalDir); + addRow("///tmp//", QStringLiteral("/tmp/"), KUriFilterData::LocalDir); + addRow("///tmp///", QStringLiteral("/tmp/"), KUriFilterData::LocalDir); if (QFile::exists(QDir::homePath() + QLatin1String("/.bashrc"))) { addRow("~/.bashrc", QDir::homePath() + QStringLiteral("/.bashrc"), KUriFilterData::LocalFile, QStringList(QStringLiteral("kshorturifilter"))); } addRow("~", QDir::homePath(), KUriFilterData::LocalDir, QStringList(QStringLiteral("kshorturifilter")), QStringLiteral("/tmp")); addRow("~bin", nullptr, KUriFilterData::LocalDir, QStringList(QStringLiteral("kshorturifilter"))); addRow("~does_not_exist", nullptr, KUriFilterData::Error, QStringList(QStringLiteral("kshorturifilter"))); addRow("~/does_not_exist", QDir::homePath() + "/does_not_exist", KUriFilterData::LocalFile, QStringList(QStringLiteral("kshorturifilter"))); // Absolute Path tests for kshorturifilter const QStringList kshorturifilter(QStringLiteral("kshorturifilter")); addRow("./", datahome, KUriFilterData::LocalDir, kshorturifilter, datahome + QStringLiteral("/")); // cleanPath removes the trailing slash const QString parentDir = QDir().cleanPath(datahome + QStringLiteral("/..")); addRow("../", QFile::encodeName(parentDir), KUriFilterData::LocalDir, kshorturifilter, datahome); addRow("share", datahome, KUriFilterData::LocalDir, kshorturifilter, QFile::encodeName(parentDir)); // Invalid URLs addRow("http://a[b]", QStringLiteral("http://a[b]"), KUriFilterData::Unknown, kshorturifilter, QStringLiteral("/")); } void KUriFilterTest::localFiles() { runFilterTest(); } void KUriFilterTest::refOrQuery_data() { setupColumns(); // URL with reference addRow("http://www.kde.org/index.html#q8", QStringLiteral("http://www.kde.org/index.html#q8"), KUriFilterData::NetProtocol); // local file with reference addRow("file:/etc/passwd#q8", QStringLiteral("file:///etc/passwd#q8"), KUriFilterData::LocalFile); addRow("file:///etc/passwd#q8", QStringLiteral("file:///etc/passwd#q8"), KUriFilterData::LocalFile); addRow("/etc/passwd#q8", QStringLiteral("file:///etc/passwd#q8"), KUriFilterData::LocalFile); // local file with query (can be used by javascript) addRow("file:/etc/passwd?foo=bar", QStringLiteral("file:///etc/passwd?foo=bar"), KUriFilterData::LocalFile); testLocalFile(QStringLiteral("/tmp/kurifiltertest?foo")); // local file with ? in the name (#58990) testLocalFile(QStringLiteral("/tmp/kurlfiltertest#foo")); // local file with '#' in the name testLocalFile(QStringLiteral("/tmp/kurlfiltertest#foo?bar")); // local file with both testLocalFile(QStringLiteral("/tmp/kurlfiltertest?foo#bar")); // local file with both, the other way round } void KUriFilterTest::refOrQuery() { runFilterTest(); } void KUriFilterTest::shortUris_data() { setupColumns(); // hostnames are lowercased by QUrl addRow("http://www.myDomain.commyPort/ViewObjectRes//Default:name=hello", - QStringLiteral("http://www.mydomain.commyport/ViewObjectRes//Default:name=hello"), KUriFilterData::NetProtocol); + QStringLiteral("http://www.mydomain.commyport/ViewObjectRes/Default:name=hello"), KUriFilterData::NetProtocol); + addRow("http://www.myDomain.commyPort/ViewObjectRes/Default:name=hello?a=a///////", + QStringLiteral("http://www.mydomain.commyport/ViewObjectRes/Default:name=hello?a=a///////"), KUriFilterData::NetProtocol); addRow("ftp://ftp.kde.org", QStringLiteral("ftp://ftp.kde.org"), KUriFilterData::NetProtocol); addRow("ftp://username@ftp.kde.org:500", QStringLiteral("ftp://username@ftp.kde.org:500"), KUriFilterData::NetProtocol); // ShortURI/LocalDomain filter tests. addRow("linuxtoday.com", QStringLiteral("http://linuxtoday.com"), KUriFilterData::NetProtocol); addRow("LINUXTODAY.COM", QStringLiteral("http://linuxtoday.com"), KUriFilterData::NetProtocol); addRow("kde.org", QStringLiteral("http://kde.org"), KUriFilterData::NetProtocol); addRow("ftp.kde.org", QStringLiteral("ftp://ftp.kde.org"), KUriFilterData::NetProtocol); addRow("ftp.kde.org:21", QStringLiteral("ftp://ftp.kde.org:21"), KUriFilterData::NetProtocol); addRow("cr.yp.to", QStringLiteral("http://cr.yp.to"), KUriFilterData::NetProtocol); addRow("www.kde.org:21", QStringLiteral("http://www.kde.org:21"), KUriFilterData::NetProtocol); // This one passes but the DNS lookup takes 5 seconds to fail //addRow("foobar.local:8000", QStringLiteral("http://foobar.local:8000"), KUriFilterData::NetProtocol); addRow("foo@bar.com", QStringLiteral("mailto:foo@bar.com"), KUriFilterData::NetProtocol); addRow("firstname.lastname@x.foo.bar", QStringLiteral("mailto:firstname.lastname@x.foo.bar"), KUriFilterData::NetProtocol); addRow("mailto:foo@bar.com", QStringLiteral("mailto:foo@bar.com"), KUriFilterData::NetProtocol); addRow("www.123.foo", QStringLiteral("http://www.123.foo"), KUriFilterData::NetProtocol); addRow("user@www.123.foo:3128", QStringLiteral("http://user@www.123.foo:3128"), KUriFilterData::NetProtocol); addRow("ftp://user@user@www.123.foo:3128", QStringLiteral("ftp://user%40user@www.123.foo:3128"), KUriFilterData::NetProtocol); addRow("user@user@www.123.foo:3128", QStringLiteral("http://user%40user@www.123.foo:3128"), KUriFilterData::NetProtocol); // IPv4 address formats... addRow("user@192.168.1.0:3128", QStringLiteral("http://user@192.168.1.0:3128"), KUriFilterData::NetProtocol); addRow("127.0.0.1", QStringLiteral("http://127.0.0.1"), KUriFilterData::NetProtocol); addRow("127.0.0.1:3128", QStringLiteral("http://127.0.0.1:3128"), KUriFilterData::NetProtocol); addRow("127.1", QStringLiteral("http://127.0.0.1"), KUriFilterData::NetProtocol); // Qt5: QUrl resolves to 127.0.0.1 addRow("127.0.1", QStringLiteral("http://127.0.0.1"), KUriFilterData::NetProtocol); // Qt5: QUrl resolves to 127.0.0.1 // IPv6 address formats (taken from RFC 2732)... addRow("[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html", QStringLiteral("http://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:80/index.html"), KUriFilterData::NetProtocol); addRow("[1080:0:0:0:8:800:200C:417A]/index.html", QStringLiteral("http://[1080::8:800:200c:417a]/index.html"), KUriFilterData::NetProtocol); // Qt5 QUrl change addRow("[3ffe:2a00:100:7031::1]", QStringLiteral("http://[3ffe:2a00:100:7031::1]"), KUriFilterData::NetProtocol); addRow("[1080::8:800:200C:417A]/foo", QStringLiteral("http://[1080::8:800:200c:417a]/foo"), KUriFilterData::NetProtocol); addRow("[::192.9.5.5]/ipng", QStringLiteral("http://[::192.9.5.5]/ipng"), KUriFilterData::NetProtocol); addRow("[::FFFF:129.144.52.38]:80/index.html", QStringLiteral("http://[::ffff:129.144.52.38]:80/index.html"), KUriFilterData::NetProtocol); addRow("[2010:836B:4179::836B:4179]", QStringLiteral("http://[2010:836b:4179::836b:4179]"), KUriFilterData::NetProtocol); // Local domain filter - If you uncomment these test, make sure you // you adjust it based on the localhost entry in your /etc/hosts file. // addRow( "localhost:3128", "http://localhost.localdomain:3128", KUriFilterData::NetProtocol ); // addRow( "localhost", "http://localhost.localdomain", KUriFilterData::NetProtocol ); // addRow( "localhost/~blah", "http://localhost.localdomain/~blah", KUriFilterData::NetProtocol ); addRow("user@host.domain", QStringLiteral("mailto:user@host.domain"), KUriFilterData::NetProtocol); // new in KDE-3.2 // Windows style SMB (UNC) URL. Should be converted into the valid smb format... addRow("\\\\mainserver\\share\\file", QStringLiteral("smb://mainserver/share/file"), KUriFilterData::NetProtocol); // KDE3: was not be filtered at all. All valid protocols of this form were be ignored. // KDE4: parsed as "network protocol", seems fine to me (DF) addRow("ftp:", QStringLiteral("ftp:"), KUriFilterData::NetProtocol); addRow("http:", QStringLiteral("http:"), KUriFilterData::NetProtocol); // The default search engine is set to 'Google' //this may fail if your DNS knows domains KDE or FTP addRow("gg:", QLatin1String(""), KUriFilterData::NetProtocol); // see bug 56218 addRow("KDE", QStringLiteral("https://www.google.com/search?q=KDE&ie=UTF-8"), KUriFilterData::NetProtocol); addRow("HTTP", QStringLiteral("https://www.google.com/search?q=HTTP&ie=UTF-8"), KUriFilterData::NetProtocol); } void KUriFilterTest::shortUris() { runFilterTest(); } void KUriFilterTest::executables_data() { setupColumns(); // Executable tests - No IKWS in minicli addRow("cp", QStringLiteral("cp"), KUriFilterData::Executable, minicliFilters); addRow("kbuildsycoca5", QStringLiteral("kbuildsycoca5"), KUriFilterData::Executable, minicliFilters); addRow("KDE", QStringLiteral("KDE"), NO_FILTERING, minicliFilters); addRow("does/not/exist", QStringLiteral("does/not/exist"), NO_FILTERING, minicliFilters); addRow("/does/not/exist", QStringLiteral("/does/not/exist"), KUriFilterData::LocalFile, minicliFilters); addRow("/does/not/exist#a", QStringLiteral("/does/not/exist#a"), KUriFilterData::LocalFile, minicliFilters); addRow("kbuildsycoca5 --help", QStringLiteral("kbuildsycoca5 --help"), KUriFilterData::Executable, minicliFilters); // the args are in argsAndOptions() addRow("/bin/sh", QStringLiteral("/bin/sh"), KUriFilterData::Executable, minicliFilters); addRow("/bin/sh -q -option arg1", QStringLiteral("/bin/sh -q -option arg1"), KUriFilterData::Executable, minicliFilters); // the args are in argsAndOptions() // Typing 'cp' or any other valid unix command in konq's location bar should result in // a search using the default search engine // 'ls' is a bit of a special case though, due to the toplevel domain called 'ls' addRow("cp", QStringLiteral("https://www.google.com/search?q=cp&ie=UTF-8"), KUriFilterData::NetProtocol, QStringList(), nullptr, false /* don't check for executables, see konq_misc.cc */); } void KUriFilterTest::executables() { runFilterTest(); } void KUriFilterTest::environmentVariables_data() { setupColumns(); // ENVIRONMENT variable qputenv("SOMEVAR", "/somevar"); qputenv("ETC", "/etc"); addRow("$SOMEVAR/kdelibs/kio", "/somevar/kdelibs/kio", KUriFilterData::LocalFile); // note: this dir doesn't exist... addRow("$ETC/passwd", QStringLiteral("/etc/passwd"), KUriFilterData::LocalFile); QString qtdocPath = qtdir + QStringLiteral("/doc/html/functions.html"); if (QFile::exists(qtdocPath)) { QString expectedUrl = QUrl::fromLocalFile(qtdocPath).toString() + "#s"; addRow("$QTDIR/doc/html/functions.html#s", expectedUrl.toUtf8(), KUriFilterData::LocalFile); } addRow("http://www.kde.org/$USER", QStringLiteral("http://www.kde.org/$USER"), KUriFilterData::NetProtocol); // no expansion addRow("$DATAHOME", datahome, KUriFilterData::LocalDir); QDir().mkpath(datahome + QStringLiteral("/urifilter/a+plus")); addRow("$DATAHOME/urifilter/a+plus", datahome + QStringLiteral("/urifilter/a+plus"), KUriFilterData::LocalDir); // BR 27788 QDir().mkpath(datahome + QStringLiteral("/Dir With Space")); addRow("$DATAHOME/Dir With Space", datahome + QStringLiteral("/Dir With Space"), KUriFilterData::LocalDir); // support for name filters (BR 93825) addRow("$DATAHOME/*.txt", datahome + QStringLiteral("/*.txt"), KUriFilterData::LocalDir); addRow("$DATAHOME/[a-b]*.txt", datahome + QStringLiteral("/[a-b]*.txt"), KUriFilterData::LocalDir); addRow("$DATAHOME/a?c.txt", datahome + QStringLiteral("/a?c.txt"), KUriFilterData::LocalDir); addRow("$DATAHOME/?c.txt", datahome + QStringLiteral("/?c.txt"), KUriFilterData::LocalDir); // but let's check that a directory with * in the name still works QDir().mkpath(datahome + QStringLiteral("/share/Dir*With*Stars")); addRow("$DATAHOME/Dir*With*Stars", datahome + QStringLiteral("/Dir*With*Stars"), KUriFilterData::LocalDir); QDir().mkpath(datahome + QStringLiteral("/Dir?QuestionMark")); addRow("$DATAHOME/Dir?QuestionMark", datahome + QStringLiteral("/Dir?QuestionMark"), KUriFilterData::LocalDir); QDir().mkpath(datahome + QStringLiteral("/Dir[Bracket")); addRow("$DATAHOME/Dir[Bracket", datahome + QStringLiteral("/Dir[Bracket"), KUriFilterData::LocalDir); addRow("$HOME/$KDEDIR/kdebase/kcontrol/ebrowsing", "", KUriFilterData::LocalFile); addRow("$1/$2/$3", QStringLiteral("https://www.google.com/search?q=%241%2F%242%2F%243&ie=UTF-8"), KUriFilterData::NetProtocol); // can be used as bogus or valid test. Currently triggers default search, i.e. google addRow("$$$$", QStringLiteral("https://www.google.com/search?q=%24%24%24%24&ie=UTF-8"), KUriFilterData::NetProtocol); // worst case scenarios. if (!qtdir.isEmpty()) { addRow("$QTDIR", qtdir, KUriFilterData::LocalDir, QStringList(QStringLiteral("kshorturifilter"))); //use specific filter. } addRow("$HOME", home, KUriFilterData::LocalDir, QStringList(QStringLiteral("kshorturifilter"))); //use specific filter. } void KUriFilterTest::environmentVariables() { runFilterTest(); } void KUriFilterTest::internetKeywords_data() { setupColumns(); QString sc; addRow(sc.sprintf("gg%cfoo bar", s_delimiter).toUtf8(), QStringLiteral("https://www.google.com/search?q=foo+bar&ie=UTF-8"), KUriFilterData::NetProtocol); addRow(sc.sprintf("bug%c55798", s_delimiter).toUtf8(), QStringLiteral("https://bugs.kde.org/show_bug.cgi?id=55798"), KUriFilterData::NetProtocol); addRow(sc.sprintf("gg%cC++", s_delimiter).toUtf8(), QStringLiteral("https://www.google.com/search?q=C%2B%2B&ie=UTF-8"), KUriFilterData::NetProtocol); addRow(sc.sprintf("gg%cC#", s_delimiter).toUtf8(), QStringLiteral("https://www.google.com/search?q=C%23&ie=UTF-8"), KUriFilterData::NetProtocol); addRow(sc.sprintf("ya%cfoo bar was here", s_delimiter).toUtf8(), nullptr, -1); // this triggers default search, i.e. google addRow(sc.sprintf("gg%cwww.kde.org", s_delimiter).toUtf8(), QStringLiteral("https://www.google.com/search?q=www.kde.org&ie=UTF-8"), KUriFilterData::NetProtocol); addRow(QStringLiteral("gg%1é").arg(s_delimiter).toUtf8() /*eaccent in utf8*/, QStringLiteral("https://www.google.com/search?q=%C3%A9&ie=UTF-8"), KUriFilterData::NetProtocol); addRow(QStringLiteral("gg%1прйвет").arg(s_delimiter).toUtf8() /* greetings in russian utf-8*/, QStringLiteral("https://www.google.com/search?q=%D0%BF%D1%80%D0%B9%D0%B2%D0%B5%D1%82&ie=UTF-8"), KUriFilterData::NetProtocol); } void KUriFilterTest::internetKeywords() { runFilterTest(); } void KUriFilterTest::localdomain() { const QString host = QHostInfo::localHostName(); if (host.isEmpty()) { const QString expected = QLatin1String("http://") + host; runFilterTest(host, expected, KUriFilterData::NetProtocol, QStringList() << QStringLiteral("localdomainurifilter"), nullptr, false); } } diff --git a/src/filewidgets/kdiroperator.cpp b/src/filewidgets/kdiroperator.cpp index 962a263e..ac062ec7 100644 --- a/src/filewidgets/kdiroperator.cpp +++ b/src/filewidgets/kdiroperator.cpp @@ -1,2723 +1,2724 @@ /* This file is part of the KDE libraries Copyright (C) 1999,2000 Stephan Kulow 1999,2000,2001,2002,2003 Carsten Pfeiffer 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 "kdiroperator.h" #include #include #include "kdirmodel.h" #include "kdiroperatordetailview_p.h" #include "kdirsortfilterproxymodel.h" #include "kfileitem.h" #include "kfilemetapreview_p.h" #include "kpreviewwidgetbase.h" #include "knewfilemenu.h" #include "../pathhelpers_p.h" #include #include // ConfigGroup, DefaultShowHidden, DefaultDirsFirst, DefaultSortReversed #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include template class QHash; // QDir::SortByMask is not only undocumented, it also omits QDir::Type which is another // sorting mode. static const int QDirSortMask = QDir::SortByMask | QDir::Type; /** * Default icon view for KDirOperator using * custom view options. */ class KDirOperatorIconView : public QListView { Q_OBJECT public: KDirOperatorIconView(KDirOperator *dirOperator, QWidget *parent = nullptr); virtual ~KDirOperatorIconView(); protected: QStyleOptionViewItem viewOptions() const override; void dragEnterEvent(QDragEnterEvent *event) override; void mousePressEvent(QMouseEvent *event) override; void wheelEvent(QWheelEvent *event) override; private: KDirOperator *ops; }; KDirOperatorIconView::KDirOperatorIconView(KDirOperator *dirOperator, QWidget *parent) : QListView(parent), ops(dirOperator) { setViewMode(QListView::IconMode); setFlow(QListView::TopToBottom); setResizeMode(QListView::Adjust); setSpacing(0); setMovement(QListView::Static); setDragDropMode(QListView::DragOnly); setVerticalScrollMode(QListView::ScrollPerPixel); setHorizontalScrollMode(QListView::ScrollPerPixel); setEditTriggers(QAbstractItemView::NoEditTriggers); setWordWrap(true); setIconSize(QSize(KIconLoader::SizeSmall, KIconLoader::SizeSmall)); } KDirOperatorIconView::~KDirOperatorIconView() { } QStyleOptionViewItem KDirOperatorIconView::viewOptions() const { QStyleOptionViewItem viewOptions = QListView::viewOptions(); viewOptions.showDecorationSelected = true; viewOptions.decorationPosition = ops->decorationPosition(); if (viewOptions.decorationPosition == QStyleOptionViewItem::Left) { viewOptions.displayAlignment = Qt::AlignLeft | Qt::AlignVCenter; } else { viewOptions.displayAlignment = Qt::AlignCenter; } return viewOptions; } void KDirOperatorIconView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { event->acceptProposedAction(); } } void KDirOperatorIconView::mousePressEvent(QMouseEvent *event) { if (!indexAt(event->pos()).isValid()) { const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); if (!(modifiers & Qt::ShiftModifier) && !(modifiers & Qt::ControlModifier)) { clearSelection(); } } QListView::mousePressEvent(event); } void KDirOperatorIconView::wheelEvent(QWheelEvent *event) { QListView::wheelEvent(event); // apply the vertical wheel event to the horizontal scrollbar, as // the items are aligned from left to right if (event->orientation() == Qt::Vertical) { QWheelEvent horizEvent(event->pos(), event->delta(), event->buttons(), event->modifiers(), Qt::Horizontal); QApplication::sendEvent(horizontalScrollBar(), &horizEvent); } } void KDirOperator::keyPressEvent(QKeyEvent *e) { if (!(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter)) { QWidget::keyPressEvent(e); } } class Q_DECL_HIDDEN KDirOperator::Private { public: Private(KDirOperator *parent); ~Private(); enum InlinePreviewState { ForcedToFalse = 0, ForcedToTrue, NotForced }; // private methods bool checkPreviewInternal() const; void checkPath(const QString &txt, bool takeFiles = false); bool openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags = KDirLister::NoFlags); int sortColumn() const; Qt::SortOrder sortOrder() const; void updateSorting(QDir::SortFlags sort); static bool isReadable(const QUrl &url); bool isSchemeSupported(const QString &scheme) const; KFile::FileView allViews(); // private slots void _k_slotDetailedView(); void _k_slotSimpleView(); void _k_slotTreeView(); void _k_slotDetailedTreeView(); void _k_slotToggleHidden(bool); void _k_togglePreview(bool); void _k_toggleInlinePreviews(bool); void _k_slotOpenFileManager(); void _k_slotSortByName(); void _k_slotSortBySize(); void _k_slotSortByDate(); void _k_slotSortByType(); void _k_slotSortReversed(bool doReverse); void _k_slotToggleDirsFirst(); void _k_slotToggleIgnoreCase(); void _k_slotStarted(); void _k_slotProgress(int); void _k_slotShowProgress(); void _k_slotIOFinished(); void _k_slotCanceled(); void _k_slotRedirected(const QUrl &); void _k_slotProperties(); void _k_slotActivated(const QModelIndex &); void _k_slotSelectionChanged(); void _k_openContextMenu(const QPoint &); void _k_triggerPreview(const QModelIndex &); void _k_showPreview(); void _k_slotSplitterMoved(int, int); void _k_assureVisibleSelection(); void _k_synchronizeSortingState(int, Qt::SortOrder); void _k_slotChangeDecorationPosition(); void _k_slotExpandToUrl(const QModelIndex &); void _k_slotItemsChanged(); void _k_slotDirectoryCreated(const QUrl &); void updateListViewGrid(); void updatePreviewActionState(); int iconSizeForViewType(QAbstractItemView *itemView) const; // private members KDirOperator *parent; QStack backStack; ///< Contains all URLs you can reach with the back button. QStack forwardStack; ///< Contains all URLs you can reach with the forward button. QModelIndex lastHoveredIndex; KDirLister *dirLister; QUrl currUrl; KCompletion completion; KCompletion dirCompletion; bool completeListDirty; QDir::SortFlags sorting; QStyleOptionViewItem::Position decorationPosition; QSplitter *splitter; QAbstractItemView *itemView; KDirModel *dirModel; KDirSortFilterProxyModel *proxyModel; KFileItemList pendingMimeTypes; // the enum KFile::FileView as an int int viewKind; int defaultView; KFile::Modes mode; QProgressBar *progressBar; KPreviewWidgetBase *preview; QUrl previewUrl; int previewWidth; bool dirHighlighting; bool onlyDoubleClickSelectsFiles; QString lastURL; // used for highlighting a directory on cdUp QTimer *progressDelayTimer; int dropOptions; KActionMenu *actionMenu; KActionCollection *actionCollection; KNewFileMenu *newFileMenu; KConfigGroup *configGroup; KFilePreviewGenerator *previewGenerator; bool showPreviews; bool calledFromUpdatePreviewActionState; bool showPreviewsConfigEntry; int iconsZoom; bool isSaving; KActionMenu *decorationMenu; KToggleAction *leftAction; QList itemsToBeSetAsCurrent; bool shouldFetchForItems; InlinePreviewState inlinePreviewState; QStringList supportedSchemes; }; KDirOperator::Private::Private(KDirOperator *_parent) : parent(_parent), dirLister(nullptr), decorationPosition(QStyleOptionViewItem::Left), splitter(nullptr), itemView(nullptr), dirModel(nullptr), proxyModel(nullptr), progressBar(nullptr), preview(nullptr), previewUrl(), previewWidth(0), dirHighlighting(false), onlyDoubleClickSelectsFiles(!qApp->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)), progressDelayTimer(nullptr), dropOptions(0), actionMenu(nullptr), actionCollection(nullptr), newFileMenu(nullptr), configGroup(nullptr), previewGenerator(nullptr), showPreviews(false), calledFromUpdatePreviewActionState(false), showPreviewsConfigEntry(false), iconsZoom(0), isSaving(false), decorationMenu(nullptr), leftAction(nullptr), shouldFetchForItems(false), inlinePreviewState(NotForced) { } KDirOperator::Private::~Private() { delete itemView; itemView = nullptr; // TODO: // if (configGroup) { // itemView->writeConfig(configGroup); // } qDeleteAll(backStack); qDeleteAll(forwardStack); delete preview; preview = nullptr; delete proxyModel; proxyModel = nullptr; delete dirModel; dirModel = nullptr; dirLister = nullptr; // deleted by KDirModel delete configGroup; configGroup = nullptr; delete progressDelayTimer; progressDelayTimer = nullptr; } KDirOperator::KDirOperator(const QUrl &_url, QWidget *parent) : QWidget(parent), d(new Private(this)) { d->splitter = new QSplitter(this); d->splitter->setChildrenCollapsible(false); connect(d->splitter, SIGNAL(splitterMoved(int,int)), this, SLOT(_k_slotSplitterMoved(int,int))); d->preview = nullptr; d->mode = KFile::File; d->viewKind = KFile::Simple; if (_url.isEmpty()) { // no dir specified -> current dir QString strPath = QDir::currentPath(); strPath.append(QChar('/')); d->currUrl = QUrl::fromLocalFile(strPath); } else { d->currUrl = _url; if (d->currUrl.scheme().isEmpty()) { d->currUrl.setScheme(QStringLiteral("file")); } QString path = d->currUrl.path(); if (!path.endsWith('/')) { path.append('/'); // make sure we have a trailing slash! } d->currUrl.setPath(path); } // We set the direction of this widget to LTR, since even on RTL desktops // viewing directory listings in RTL mode makes people's head explode. // Is this the correct place? Maybe it should be in some lower level widgets...? setLayoutDirection(Qt::LeftToRight); setDirLister(new KDirLister()); connect(&d->completion, SIGNAL(match(QString)), SLOT(slotCompletionMatch(QString))); d->progressBar = new QProgressBar(this); d->progressBar->setObjectName(QStringLiteral("d->progressBar")); d->progressBar->adjustSize(); d->progressBar->move(2, height() - d->progressBar->height() - 2); d->progressDelayTimer = new QTimer(this); d->progressDelayTimer->setObjectName(QStringLiteral("d->progressBar delay timer")); connect(d->progressDelayTimer, SIGNAL(timeout()), SLOT(_k_slotShowProgress())); d->completeListDirty = false; // action stuff setupActions(); setupMenu(); d->sorting = QDir::NoSort; //so updateSorting() doesn't think nothing has changed d->updateSorting(QDir::Name | QDir::DirsFirst); setFocusPolicy(Qt::WheelFocus); } KDirOperator::~KDirOperator() { resetCursor(); disconnect(d->dirLister, nullptr, this, nullptr); delete d; } void KDirOperator::setSorting(QDir::SortFlags spec) { d->updateSorting(spec); } QDir::SortFlags KDirOperator::sorting() const { return d->sorting; } bool KDirOperator::isRoot() const { #ifdef Q_OS_WIN if (url().isLocalFile()) { const QString path = url().toLocalFile(); if (path.length() == 3) { return (path[0].isLetter() && path[1] == ':' && path[2] == '/'); } return false; } else #endif return url().path() == QString(QLatin1Char('/')); } KDirLister *KDirOperator::dirLister() const { return d->dirLister; } void KDirOperator::resetCursor() { if (qApp) { QApplication::restoreOverrideCursor(); } d->progressBar->hide(); } void KDirOperator::sortByName() { d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Name); } void KDirOperator::sortBySize() { d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Size); } void KDirOperator::sortByDate() { d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Time); } void KDirOperator::sortByType() { d->updateSorting((d->sorting & ~QDirSortMask) | QDir::Type); } void KDirOperator::sortReversed() { // toggle it, hence the inversion of current state d->_k_slotSortReversed(!(d->sorting & QDir::Reversed)); } void KDirOperator::toggleDirsFirst() { d->_k_slotToggleDirsFirst(); } void KDirOperator::toggleIgnoreCase() { if (d->proxyModel != nullptr) { Qt::CaseSensitivity cs = d->proxyModel->sortCaseSensitivity(); cs = (cs == Qt::CaseSensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive; d->proxyModel->setSortCaseSensitivity(cs); } } void KDirOperator::updateSelectionDependentActions() { const bool hasSelection = (d->itemView != nullptr) && d->itemView->selectionModel()->hasSelection(); d->actionCollection->action(QStringLiteral("trash"))->setEnabled(hasSelection); d->actionCollection->action(QStringLiteral("delete"))->setEnabled(hasSelection); d->actionCollection->action(QStringLiteral("properties"))->setEnabled(hasSelection); } void KDirOperator::setPreviewWidget(KPreviewWidgetBase *w) { const bool showPreview = (w != nullptr); if (showPreview) { d->viewKind = (d->viewKind | KFile::PreviewContents); } else { d->viewKind = (d->viewKind & ~KFile::PreviewContents); } delete d->preview; d->preview = w; if (w) { d->splitter->addWidget(w); } KToggleAction *previewAction = static_cast(d->actionCollection->action(QStringLiteral("preview"))); previewAction->setEnabled(showPreview); previewAction->setChecked(showPreview); setView(static_cast(d->viewKind)); } KFileItemList KDirOperator::selectedItems() const { KFileItemList itemList; if (d->itemView == nullptr) { return itemList; } const QItemSelection selection = d->proxyModel->mapSelectionToSource(d->itemView->selectionModel()->selection()); const QModelIndexList indexList = selection.indexes(); foreach (const QModelIndex &index, indexList) { KFileItem item = d->dirModel->itemForIndex(index); if (!item.isNull()) { itemList.append(item); } } return itemList; } bool KDirOperator::isSelected(const KFileItem &item) const { if ((item.isNull()) || (d->itemView == nullptr)) { return false; } const QModelIndex dirIndex = d->dirModel->indexForItem(item); const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex); return d->itemView->selectionModel()->isSelected(proxyIndex); } int KDirOperator::numDirs() const { return (d->dirLister == nullptr) ? 0 : d->dirLister->directories().count(); } int KDirOperator::numFiles() const { return (d->dirLister == nullptr) ? 0 : d->dirLister->items().count() - numDirs(); } KCompletion *KDirOperator::completionObject() const { return const_cast(&d->completion); } KCompletion *KDirOperator::dirCompletionObject() const { return const_cast(&d->dirCompletion); } KActionCollection *KDirOperator::actionCollection() const { return d->actionCollection; } KFile::FileView KDirOperator::Private::allViews() { return static_cast(KFile::Simple | KFile::Detail | KFile::Tree | KFile::DetailTree); } void KDirOperator::Private::_k_slotDetailedView() { KFile::FileView view = static_cast((viewKind & ~allViews()) | KFile::Detail); parent->setView(view); } void KDirOperator::Private::_k_slotSimpleView() { KFile::FileView view = static_cast((viewKind & ~allViews()) | KFile::Simple); parent->setView(view); } void KDirOperator::Private::_k_slotTreeView() { KFile::FileView view = static_cast((viewKind & ~allViews()) | KFile::Tree); parent->setView(view); } void KDirOperator::Private::_k_slotDetailedTreeView() { KFile::FileView view = static_cast((viewKind & ~allViews()) | KFile::DetailTree); parent->setView(view); } void KDirOperator::Private::_k_slotToggleHidden(bool show) { dirLister->setShowingDotFiles(show); parent->updateDir(); _k_assureVisibleSelection(); } void KDirOperator::Private::_k_togglePreview(bool on) { if (on) { viewKind = viewKind | KFile::PreviewContents; if (preview == nullptr) { preview = new KFileMetaPreview(parent); actionCollection->action(QStringLiteral("preview"))->setChecked(true); splitter->addWidget(preview); } preview->show(); QMetaObject::invokeMethod(parent, "_k_assureVisibleSelection", Qt::QueuedConnection); if (itemView != nullptr) { const QModelIndex index = itemView->selectionModel()->currentIndex(); if (index.isValid()) { _k_triggerPreview(index); } } } else if (preview != nullptr) { viewKind = viewKind & ~KFile::PreviewContents; preview->hide(); } } void KDirOperator::Private::_k_toggleInlinePreviews(bool show) { if (showPreviews == show) { return; } showPreviews = show; if (!calledFromUpdatePreviewActionState) { showPreviewsConfigEntry = show; } if (!previewGenerator) { return; } previewGenerator->setPreviewShown(show); } void KDirOperator::Private::_k_slotOpenFileManager() { const KFileItemList list = parent->selectedItems(); if (list.isEmpty()) { KIO::highlightInFileManager({currUrl.adjusted(QUrl::StripTrailingSlash)}); } else { KIO::highlightInFileManager(list.urlList()); } } void KDirOperator::Private::_k_slotSortByName() { parent->sortByName(); } void KDirOperator::Private::_k_slotSortBySize() { parent->sortBySize(); } void KDirOperator::Private::_k_slotSortByDate() { parent->sortByDate(); } void KDirOperator::Private::_k_slotSortByType() { parent->sortByType(); } void KDirOperator::Private::_k_slotSortReversed(bool doReverse) { QDir::SortFlags s = sorting & ~QDir::Reversed; if (doReverse) { s |= QDir::Reversed; } updateSorting(s); } void KDirOperator::Private::_k_slotToggleDirsFirst() { QDir::SortFlags s = (sorting ^ QDir::DirsFirst); updateSorting(s); } void KDirOperator::Private::_k_slotToggleIgnoreCase() { // TODO: port to Qt4's QAbstractItemView /*if ( !d->fileView ) return; QDir::SortFlags sorting = d->fileView->sorting(); if ( !KFile::isSortCaseInsensitive( sorting ) ) d->fileView->setSorting( sorting | QDir::IgnoreCase ); else d->fileView->setSorting( sorting & ~QDir::IgnoreCase ); d->sorting = d->fileView->sorting();*/ } void KDirOperator::mkdir() { d->newFileMenu->setPopupFiles(QList() << url()); d->newFileMenu->setViewShowsHiddenFiles(showHiddenFiles()); d->newFileMenu->createDirectory(); } bool KDirOperator::mkdir(const QString &directory, bool enterDirectory) { // Creates "directory", relative to the current directory (d->currUrl). // The given path may contain any number directories, existent or not. // They will all be created, if possible. // TODO: very similar to KDirSelectDialog::Private::slotMkdir bool writeOk = false; bool exists = false; QUrl folderurl(d->currUrl); const QStringList dirs = directory.split('/', QString::SkipEmptyParts); QStringList::ConstIterator it = dirs.begin(); for (; it != dirs.end(); ++it) { folderurl.setPath(concatPaths(folderurl.path(), *it)); if (folderurl.isLocalFile()) { exists = QFile::exists(folderurl.toLocalFile()); } else { KIO::StatJob *job = KIO::stat(folderurl); KJobWidgets::setWindow(job, this); job->setDetails(0); //We only want to know if it exists, 0 == that. job->setSide(KIO::StatJob::DestinationSide); exists = job->exec(); } if (!exists) { KIO::Job *job = KIO::mkdir(folderurl); KJobWidgets::setWindow(job, this); writeOk = job->exec(); } } if (exists) { // url was already existent KMessageBox::sorry(d->itemView, i18n("A file or folder named %1 already exists.", folderurl.toDisplayString(QUrl::PreferLocalFile))); } else if (!writeOk) { KMessageBox::sorry(d->itemView, i18n("You do not have permission to " "create that folder.")); } else if (enterDirectory) { setUrl(folderurl, true); } return writeOk; } KIO::DeleteJob *KDirOperator::del(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress) { if (items.isEmpty()) { KMessageBox::information(parent, i18n("You did not select a file to delete."), i18n("Nothing to Delete")); return nullptr; } if (parent == nullptr) { parent = this; } const QList urls = items.urlList(); bool doIt = !ask; if (ask) { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(parent); doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation); } if (doIt) { KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo; KIO::DeleteJob *job = KIO::del(urls, flags); KJobWidgets::setWindow(job, this); job->uiDelegate()->setAutoErrorHandlingEnabled(true); return job; } return nullptr; } void KDirOperator::deleteSelected() { const KFileItemList list = selectedItems(); if (!list.isEmpty()) { del(list, this); } } KIO::CopyJob *KDirOperator::trash(const KFileItemList &items, QWidget *parent, bool ask, bool showProgress) { if (items.isEmpty()) { KMessageBox::information(parent, i18n("You did not select a file to trash."), i18n("Nothing to Trash")); return nullptr; } const QList urls = items.urlList(); bool doIt = !ask; if (ask) { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(parent); doIt = uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation); } if (doIt) { KIO::JobFlags flags = showProgress ? KIO::DefaultFlags : KIO::HideProgressInfo; KIO::CopyJob *job = KIO::trash(urls, flags); KJobWidgets::setWindow(job, this); job->uiDelegate()->setAutoErrorHandlingEnabled(true); return job; } return nullptr; } KFilePreviewGenerator *KDirOperator::previewGenerator() const { return d->previewGenerator; } void KDirOperator::setInlinePreviewShown(bool show) { d->inlinePreviewState = show ? Private::ForcedToTrue : Private::ForcedToFalse; } bool KDirOperator::isInlinePreviewShown() const { return d->showPreviews; } int KDirOperator::iconsZoom() const { return d->iconsZoom; } void KDirOperator::setIsSaving(bool isSaving) { d->isSaving = isSaving; } bool KDirOperator::isSaving() const { return d->isSaving; } void KDirOperator::trashSelected() { if (d->itemView == nullptr) { return; } if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { deleteSelected(); return; } const KFileItemList list = selectedItems(); if (!list.isEmpty()) { trash(list, this); } } void KDirOperator::setIconsZoom(int _value) { if (d->iconsZoom == _value) { return; } int value = _value; value = qMin(100, value); value = qMax(0, value); d->iconsZoom = value; if (!d->previewGenerator) { return; } const int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall; const int val = (maxSize * value / 100) + KIconLoader::SizeSmall; d->itemView->setIconSize(QSize(val, val)); d->updateListViewGrid(); d->previewGenerator->updatePreviews(); emit currentIconSizeChanged(value); } void KDirOperator::close() { resetCursor(); d->pendingMimeTypes.clear(); d->completion.clear(); d->dirCompletion.clear(); d->completeListDirty = true; d->dirLister->stop(); } void KDirOperator::Private::checkPath(const QString &, bool /*takeFiles*/) // SLOT { #if 0 // copy the argument in a temporary string QString text = _txt; // it's unlikely to happen, that at the beginning are spaces, but // for the end, it happens quite often, I guess. text = text.trimmed(); // if the argument is no URL (the check is quite fragil) and it's // no absolute path, we add the current directory to get a correct url if (text.find(':') < 0 && text[0] != '/') { text.insert(0, d->currUrl); } // in case we have a selection defined and someone patched the file- // name, we check, if the end of the new name is changed. if (!selection.isNull()) { int position = text.lastIndexOf('/'); ASSERT(position >= 0); // we already inserted the current d->dirLister in case QString filename = text.mid(position + 1, text.length()); if (filename != selection) { selection.clear(); } } QUrl u(text); // I have to take care of entered URLs bool filenameEntered = false; if (u.isLocalFile()) { // the empty path is kind of a hack KFileItem i("", u.toLocalFile()); if (i.isDir()) { setUrl(text, true); } else { if (takeFiles) if (acceptOnlyExisting && !i.isFile()) { warning("you entered an invalid URL"); } else { filenameEntered = true; } } } else { setUrl(text, true); } if (filenameEntered) { filename_ = u.url(); emit fileSelected(filename_); QApplication::restoreOverrideCursor(); accept(); } #endif // qDebug() << "TODO KDirOperator::checkPath()"; } void KDirOperator::setUrl(const QUrl &_newurl, bool clearforward) { QUrl newurl; if (!_newurl.isValid()) { newurl = QUrl::fromLocalFile(QDir::homePath()); } else { - newurl = _newurl; + newurl = _newurl.adjusted(QUrl::NormalizePathSegments); } if (!newurl.path().isEmpty() && !newurl.path().endsWith(QLatin1Char('/'))) { newurl.setPath(newurl.path() + QLatin1Char('/')); } // already set if (newurl.matches(d->currUrl, QUrl::StripTrailingSlash)) { return; } if (!d->isSchemeSupported(newurl.scheme())) return; if (!Private::isReadable(newurl)) { // maybe newurl is a file? check its parent directory newurl = newurl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); if (newurl.matches(d->currUrl, QUrl::StripTrailingSlash)) { return; // parent is current dir, nothing to do (fixes #173454, too) } KIO::StatJob *job = KIO::stat(newurl); KJobWidgets::setWindow(job, this); bool res = job->exec(); KIO::UDSEntry entry = job->statResult(); KFileItem i(entry, newurl); if ((!res || !Private::isReadable(newurl)) && i.isDir()) { resetCursor(); KMessageBox::error(d->itemView, i18n("The specified folder does not exist " "or was not readable.")); return; } else if (!i.isDir()) { return; } } if (clearforward) { // autodelete should remove this one d->backStack.push(new QUrl(d->currUrl)); qDeleteAll(d->forwardStack); d->forwardStack.clear(); } d->lastURL = d->currUrl.toString(QUrl::StripTrailingSlash); d->currUrl = newurl; pathChanged(); emit urlEntered(newurl); // enable/disable actions QAction *forwardAction = d->actionCollection->action(QStringLiteral("forward")); forwardAction->setEnabled(!d->forwardStack.isEmpty()); QAction *backAction = d->actionCollection->action(QStringLiteral("back")); backAction->setEnabled(!d->backStack.isEmpty()); QAction *upAction = d->actionCollection->action(QStringLiteral("up")); upAction->setEnabled(!isRoot()); d->openUrl(newurl); } void KDirOperator::updateDir() { QApplication::setOverrideCursor(Qt::WaitCursor); d->dirLister->emitChanges(); QApplication::restoreOverrideCursor(); } void KDirOperator::rereadDir() { pathChanged(); d->openUrl(d->currUrl, KDirLister::Reload); } bool KDirOperator::Private::isSchemeSupported(const QString &scheme) const { return supportedSchemes.isEmpty() || supportedSchemes.contains(scheme); } bool KDirOperator::Private::openUrl(const QUrl &url, KDirLister::OpenUrlFlags flags) { const bool result = KProtocolManager::supportsListing(url) && isSchemeSupported(url.scheme()) && dirLister->openUrl(url, flags); if (!result) { // in that case, neither completed() nor canceled() will be emitted by KDL _k_slotCanceled(); } return result; } int KDirOperator::Private::sortColumn() const { int column = KDirModel::Name; if (KFile::isSortByDate(sorting)) { column = KDirModel::ModifiedTime; } else if (KFile::isSortBySize(sorting)) { column = KDirModel::Size; } else if (KFile::isSortByType(sorting)) { column = KDirModel::Type; } else { Q_ASSERT(KFile::isSortByName(sorting)); } return column; } Qt::SortOrder KDirOperator::Private::sortOrder() const { return (sorting & QDir::Reversed) ? Qt::DescendingOrder : Qt::AscendingOrder; } void KDirOperator::Private::updateSorting(QDir::SortFlags sort) { // qDebug() << "changing sort flags from" << sorting << "to" << sort; if (sort == sorting) { return; } if ((sorting ^ sort) & QDir::DirsFirst) { // The "Folders First" setting has been changed. // We need to make sure that the files and folders are really re-sorted. // Without the following intermediate "fake resorting", // QSortFilterProxyModel::sort(int column, Qt::SortOrder order) // would do nothing because neither the column nor the sort order have been changed. Qt::SortOrder tmpSortOrder = (sortOrder() == Qt::AscendingOrder ? Qt::DescendingOrder : Qt::AscendingOrder); proxyModel->sort(sortOrder(), tmpSortOrder); proxyModel->setSortFoldersFirst(sort & QDir::DirsFirst); } sorting = sort; parent->updateSortActions(); proxyModel->sort(sortColumn(), sortOrder()); // TODO: The headers from QTreeView don't take care about a sorting // change of the proxy model hence they must be updated the manually. // This is done here by a qobject_cast, but it would be nicer to: // - provide a signal 'sortingChanged()' // - connect KDirOperatorDetailView() with this signal and update the // header internally QTreeView *treeView = qobject_cast(itemView); if (treeView != nullptr) { QHeaderView *headerView = treeView->header(); headerView->blockSignals(true); headerView->setSortIndicator(sortColumn(), sortOrder()); headerView->blockSignals(false); } _k_assureVisibleSelection(); } // Protected void KDirOperator::pathChanged() { if (d->itemView == nullptr) { return; } d->pendingMimeTypes.clear(); //d->fileView->clear(); TODO d->completion.clear(); d->dirCompletion.clear(); // it may be, that we weren't ready at this time QApplication::restoreOverrideCursor(); // when KIO::Job emits finished, the slot will restore the cursor QApplication::setOverrideCursor(Qt::WaitCursor); if (!Private::isReadable(d->currUrl)) { KMessageBox::error(d->itemView, i18n("The specified folder does not exist " "or was not readable.")); if (d->backStack.isEmpty()) { home(); } else { back(); } } } void KDirOperator::Private::_k_slotRedirected(const QUrl &newURL) { currUrl = newURL; pendingMimeTypes.clear(); completion.clear(); dirCompletion.clear(); completeListDirty = true; emit parent->urlEntered(newURL); } // Code pinched from kfm then hacked void KDirOperator::back() { if (d->backStack.isEmpty()) { return; } d->forwardStack.push(new QUrl(d->currUrl)); QUrl *s = d->backStack.pop(); setUrl(*s, false); delete s; } // Code pinched from kfm then hacked void KDirOperator::forward() { if (d->forwardStack.isEmpty()) { return; } d->backStack.push(new QUrl(d->currUrl)); QUrl *s = d->forwardStack.pop(); setUrl(*s, false); delete s; } QUrl KDirOperator::url() const { return d->currUrl; } void KDirOperator::cdUp() { - QUrl tmp(d->currUrl); + // Allow /d/c// to go up to /d/ instead of /d/c/ + QUrl tmp(d->currUrl.adjusted(QUrl::NormalizePathSegments)); setUrl(tmp.resolved(QUrl(QStringLiteral(".."))), true); } void KDirOperator::home() { setUrl(QUrl::fromLocalFile(QDir::homePath()), true); } void KDirOperator::clearFilter() { d->dirLister->setNameFilter(QString()); d->dirLister->clearMimeFilter(); checkPreviewSupport(); } void KDirOperator::setNameFilter(const QString &filter) { d->dirLister->setNameFilter(filter); checkPreviewSupport(); } QString KDirOperator::nameFilter() const { return d->dirLister->nameFilter(); } void KDirOperator::setMimeFilter(const QStringList &mimetypes) { d->dirLister->setMimeFilter(mimetypes); checkPreviewSupport(); } QStringList KDirOperator::mimeFilter() const { return d->dirLister->mimeFilters(); } void KDirOperator::setNewFileMenuSupportedMimeTypes(const QStringList &mimeTypes) { d->newFileMenu->setSupportedMimeTypes(mimeTypes); } QStringList KDirOperator::newFileMenuSupportedMimeTypes() const { return d->newFileMenu->supportedMimeTypes(); } bool KDirOperator::checkPreviewSupport() { KToggleAction *previewAction = static_cast(d->actionCollection->action(QStringLiteral("preview"))); bool hasPreviewSupport = false; KConfigGroup cg(KSharedConfig::openConfig(), ConfigGroup); if (cg.readEntry("Show Default Preview", true)) { hasPreviewSupport = d->checkPreviewInternal(); } previewAction->setEnabled(hasPreviewSupport); return hasPreviewSupport; } void KDirOperator::activatedMenu(const KFileItem &item, const QPoint &pos) { updateSelectionDependentActions(); d->newFileMenu->setPopupFiles(QList() << item.url()); d->newFileMenu->setViewShowsHiddenFiles(showHiddenFiles()); d->newFileMenu->checkUpToDate(); emit contextMenuAboutToShow(item, d->actionMenu->menu()); d->actionMenu->menu()->exec(pos); } void KDirOperator::changeEvent(QEvent *event) { QWidget::changeEvent(event); } bool KDirOperator::eventFilter(QObject *watched, QEvent *event) { Q_UNUSED(watched); // If we are not hovering any items, check if there is a current index // set. In that case, we show the preview of that item. switch (event->type()) { case QEvent::MouseMove: { if (d->preview && !d->preview->isHidden()) { const QModelIndex hoveredIndex = d->itemView->indexAt(d->itemView->viewport()->mapFromGlobal(QCursor::pos())); if (d->lastHoveredIndex == hoveredIndex) { return QWidget::eventFilter(watched, event); } d->lastHoveredIndex = hoveredIndex; const QModelIndex focusedIndex = d->itemView->selectionModel() ? d->itemView->selectionModel()->currentIndex() : QModelIndex(); if (!hoveredIndex.isValid() && focusedIndex.isValid() && d->itemView->selectionModel()->isSelected(focusedIndex) && (d->lastHoveredIndex != focusedIndex)) { const QModelIndex sourceFocusedIndex = d->proxyModel->mapToSource(focusedIndex); const KFileItem item = d->dirModel->itemForIndex(sourceFocusedIndex); if (!item.isNull()) { d->preview->showPreview(item.url()); } } } } break; case QEvent::MouseButtonRelease: { if (d->preview != nullptr && !d->preview->isHidden()) { const QModelIndex hoveredIndex = d->itemView->indexAt(d->itemView->viewport()->mapFromGlobal(QCursor::pos())); const QModelIndex focusedIndex = d->itemView->selectionModel() ? d->itemView->selectionModel()->currentIndex() : QModelIndex(); if (((!focusedIndex.isValid()) || !d->itemView->selectionModel()->isSelected(focusedIndex)) && (!hoveredIndex.isValid())) { d->preview->clearPreview(); } } QMouseEvent *mouseEvent = static_cast(event); if (mouseEvent) { switch (mouseEvent->button()) { case Qt::BackButton: back(); break; case Qt::ForwardButton: forward(); break; default: break; } } } break; case QEvent::Wheel: { QWheelEvent *evt = static_cast(event); if (evt->modifiers() & Qt::ControlModifier) { if (evt->delta() > 0) { setIconsZoom(d->iconsZoom + 10); } else { setIconsZoom(d->iconsZoom - 10); } return true; } } break; default: break; } return QWidget::eventFilter(watched, event); } bool KDirOperator::Private::checkPreviewInternal() const { const QStringList supported = KIO::PreviewJob::supportedMimeTypes(); // no preview support for directories? if (parent->dirOnlyMode() && supported.indexOf(QStringLiteral("inode/directory")) == -1) { return false; } QStringList mimeTypes = dirLister->mimeFilters(); const QStringList nameFilter = dirLister->nameFilter().split(' ', QString::SkipEmptyParts); QMimeDatabase db; if (mimeTypes.isEmpty() && nameFilter.isEmpty() && !supported.isEmpty()) { return true; } else { QRegExp r; r.setPatternSyntax(QRegExp::Wildcard); // the "mimetype" can be "image/*" if (!mimeTypes.isEmpty()) { QStringList::ConstIterator it = supported.begin(); for (; it != supported.end(); ++it) { r.setPattern(*it); QStringList result = mimeTypes.filter(r); if (!result.isEmpty()) { // matches! -> we want previews return true; } } } if (!nameFilter.isEmpty()) { // find the mimetypes of all the filter-patterns QStringList::const_iterator it1 = nameFilter.begin(); for (; it1 != nameFilter.end(); ++it1) { if ((*it1) == QLatin1String("*")) { return true; } QMimeType mt = db.mimeTypeForFile(*it1, QMimeDatabase::MatchExtension /*fast mode, no file contents exist*/); if (!mt.isValid()) { continue; } QString mime = mt.name(); // the "mimetypes" we get from the PreviewJob can be "image/*" // so we need to check in wildcard mode QStringList::ConstIterator it2 = supported.begin(); for (; it2 != supported.end(); ++it2) { r.setPattern(*it2); if (r.indexIn(mime) != -1) { return true; } } } } } return false; } QAbstractItemView *KDirOperator::createView(QWidget *parent, KFile::FileView viewKind) { QAbstractItemView *itemView = nullptr; if (KFile::isDetailView(viewKind) || KFile::isTreeView(viewKind) || KFile::isDetailTreeView(viewKind)) { KDirOperatorDetailView *detailView = new KDirOperatorDetailView(parent); detailView->setViewMode(viewKind); itemView = detailView; } else { itemView = new KDirOperatorIconView(this, parent); } return itemView; } void KDirOperator::setAcceptDrops(bool b) { // TODO: //if (d->fileView) // d->fileView->widget()->setAcceptDrops(b); QWidget::setAcceptDrops(b); } void KDirOperator::setDropOptions(int options) { d->dropOptions = options; // TODO: //if (d->fileView) // d->fileView->setDropOptions(options); } void KDirOperator::setView(KFile::FileView viewKind) { bool preview = (KFile::isPreviewInfo(viewKind) || KFile::isPreviewContents(viewKind)); if (viewKind == KFile::Default) { if (KFile::isDetailView((KFile::FileView)d->defaultView)) { viewKind = KFile::Detail; } else if (KFile::isTreeView((KFile::FileView)d->defaultView)) { viewKind = KFile::Tree; } else if (KFile::isDetailTreeView((KFile::FileView)d->defaultView)) { viewKind = KFile::DetailTree; } else { viewKind = KFile::Simple; } const KFile::FileView defaultViewKind = static_cast(d->defaultView); preview = (KFile::isPreviewInfo(defaultViewKind) || KFile::isPreviewContents(defaultViewKind)) && d->actionCollection->action(QStringLiteral("preview"))->isEnabled(); } d->viewKind = static_cast(viewKind); viewKind = static_cast(d->viewKind); QAbstractItemView *newView = createView(this, viewKind); setView(newView); d->_k_togglePreview(preview); } KFile::FileView KDirOperator::viewMode() const { return static_cast(d->viewKind); } QAbstractItemView *KDirOperator::view() const { return d->itemView; } KFile::Modes KDirOperator::mode() const { return d->mode; } void KDirOperator::setMode(KFile::Modes mode) { if (d->mode == mode) { return; } d->mode = mode; d->dirLister->setDirOnlyMode(dirOnlyMode()); // reset the view with the different mode if (d->itemView != nullptr) { setView(static_cast(d->viewKind)); } } void KDirOperator::setView(QAbstractItemView *view) { if (view == d->itemView) { return; } // TODO: do a real timer and restart it after that d->pendingMimeTypes.clear(); const bool listDir = (d->itemView == nullptr); if (d->mode & KFile::Files) { view->setSelectionMode(QAbstractItemView::ExtendedSelection); } else { view->setSelectionMode(QAbstractItemView::SingleSelection); } QItemSelectionModel *selectionModel = nullptr; if ((d->itemView != nullptr) && d->itemView->selectionModel()->hasSelection()) { // remember the selection of the current item view and apply this selection // to the new view later const QItemSelection selection = d->itemView->selectionModel()->selection(); selectionModel = new QItemSelectionModel(d->proxyModel, this); selectionModel->select(selection, QItemSelectionModel::Select); } setFocusProxy(nullptr); delete d->itemView; d->itemView = view; d->itemView->setModel(d->proxyModel); setFocusProxy(d->itemView); view->viewport()->installEventFilter(this); KFileItemDelegate *delegate = new KFileItemDelegate(d->itemView); d->itemView->setItemDelegate(delegate); d->itemView->viewport()->setAttribute(Qt::WA_Hover); d->itemView->setContextMenuPolicy(Qt::CustomContextMenu); d->itemView->setMouseTracking(true); //d->itemView->setDropOptions(d->dropOptions); // first push our settings to the view, then listen for changes from the view QTreeView *treeView = qobject_cast(d->itemView); if (treeView) { QHeaderView *headerView = treeView->header(); headerView->setSortIndicator(d->sortColumn(), d->sortOrder()); connect(headerView, SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(_k_synchronizeSortingState(int,Qt::SortOrder))); } connect(d->itemView, SIGNAL(activated(QModelIndex)), this, SLOT(_k_slotActivated(QModelIndex))); connect(d->itemView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(_k_openContextMenu(QPoint))); connect(d->itemView, SIGNAL(entered(QModelIndex)), this, SLOT(_k_triggerPreview(QModelIndex))); updateViewActions(); d->splitter->insertWidget(0, d->itemView); d->splitter->resize(size()); d->itemView->show(); if (listDir) { QApplication::setOverrideCursor(Qt::WaitCursor); d->openUrl(d->currUrl); } if (selectionModel != nullptr) { d->itemView->setSelectionModel(selectionModel); QMetaObject::invokeMethod(this, "_k_assureVisibleSelection", Qt::QueuedConnection); } connect(d->itemView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(_k_triggerPreview(QModelIndex))); connect(d->itemView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(_k_slotSelectionChanged())); // if we cannot cast it to a QListView, disable the "Icon Position" menu. Note that this check // needs to be done here, and not in createView, since we can be set an external view d->decorationMenu->setEnabled(qobject_cast(d->itemView)); d->shouldFetchForItems = qobject_cast(view); if (d->shouldFetchForItems) { connect(d->dirModel, SIGNAL(expand(QModelIndex)), this, SLOT(_k_slotExpandToUrl(QModelIndex))); } else { d->itemsToBeSetAsCurrent.clear(); } const bool previewForcedToTrue = d->inlinePreviewState == Private::ForcedToTrue; const bool previewShown = d->inlinePreviewState == Private::NotForced ? d->showPreviews : previewForcedToTrue; d->previewGenerator = new KFilePreviewGenerator(d->itemView); const int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall; const int val = (maxSize * d->iconsZoom / 100) + KIconLoader::SizeSmall; d->itemView->setIconSize(previewForcedToTrue ? QSize(KIconLoader::SizeHuge, KIconLoader::SizeHuge) : QSize(val, val)); d->previewGenerator->setPreviewShown(previewShown); d->actionCollection->action(QStringLiteral("inline preview"))->setChecked(previewShown); // ensure we change everything needed d->_k_slotChangeDecorationPosition(); emit viewChanged(view); const int zoom = previewForcedToTrue ? (KIconLoader::SizeHuge - KIconLoader::SizeSmall + 1) * 100 / maxSize : d->iconSizeForViewType(view); // this will make d->iconsZoom be updated, since setIconsZoom slot will be called emit currentIconSizeChanged(zoom); } void KDirOperator::setDirLister(KDirLister *lister) { if (lister == d->dirLister) { // sanity check return; } delete d->dirModel; d->dirModel = nullptr; delete d->proxyModel; d->proxyModel = nullptr; //delete d->dirLister; // deleted by KDirModel already, which took ownership d->dirLister = lister; d->dirModel = new KDirModel(); d->dirModel->setDirLister(d->dirLister); d->dirModel->setDropsAllowed(KDirModel::DropOnDirectory); d->shouldFetchForItems = qobject_cast(d->itemView); if (d->shouldFetchForItems) { connect(d->dirModel, SIGNAL(expand(QModelIndex)), this, SLOT(_k_slotExpandToUrl(QModelIndex))); } else { d->itemsToBeSetAsCurrent.clear(); } d->proxyModel = new KDirSortFilterProxyModel(this); d->proxyModel->setSourceModel(d->dirModel); d->dirLister->setDelayedMimeTypes(true); QWidget *mainWidget = topLevelWidget(); d->dirLister->setMainWindow(mainWidget); // qDebug() << "mainWidget=" << mainWidget; connect(d->dirLister, SIGNAL(percent(int)), SLOT(_k_slotProgress(int))); connect(d->dirLister, SIGNAL(started(QUrl)), SLOT(_k_slotStarted())); connect(d->dirLister, SIGNAL(completed()), SLOT(_k_slotIOFinished())); connect(d->dirLister, SIGNAL(canceled()), SLOT(_k_slotCanceled())); connect(d->dirLister, SIGNAL(redirection(QUrl)), SLOT(_k_slotRedirected(QUrl))); connect(d->dirLister, SIGNAL(newItems(KFileItemList)), SLOT(_k_slotItemsChanged())); connect(d->dirLister, SIGNAL(itemsDeleted(KFileItemList)), SLOT(_k_slotItemsChanged())); connect(d->dirLister, SIGNAL(itemsFilteredByMime(KFileItemList)), SLOT(_k_slotItemsChanged())); connect(d->dirLister, SIGNAL(clear()), SLOT(_k_slotItemsChanged())); } void KDirOperator::selectDir(const KFileItem &item) { setUrl(item.targetUrl(), true); } void KDirOperator::selectFile(const KFileItem &item) { QApplication::restoreOverrideCursor(); emit fileSelected(item); } void KDirOperator::highlightFile(const KFileItem &item) { if ((d->preview != nullptr && !d->preview->isHidden()) && !item.isNull()) { d->preview->showPreview(item.url()); } emit fileHighlighted(item); } void KDirOperator::setCurrentItem(const QUrl &url) { // qDebug(); KFileItem item = d->dirLister->findByUrl(url); if (d->shouldFetchForItems && item.isNull()) { d->itemsToBeSetAsCurrent << url; d->dirModel->expandToUrl(url); return; } setCurrentItem(item); } void KDirOperator::setCurrentItem(const KFileItem &item) { // qDebug(); if (!d->itemView) { return; } QItemSelectionModel *selModel = d->itemView->selectionModel(); if (selModel) { selModel->clear(); if (!item.isNull()) { const QModelIndex dirIndex = d->dirModel->indexForItem(item); const QModelIndex proxyIndex = d->proxyModel->mapFromSource(dirIndex); selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select); } } } void KDirOperator::setCurrentItems(const QList &urls) { // qDebug(); if (!d->itemView) { return; } KFileItemList itemList; foreach (const QUrl &url, urls) { KFileItem item = d->dirLister->findByUrl(url); if (d->shouldFetchForItems && item.isNull()) { d->itemsToBeSetAsCurrent << url; d->dirModel->expandToUrl(url); continue; } itemList << item; } setCurrentItems(itemList); } void KDirOperator::setCurrentItems(const KFileItemList &items) { // qDebug(); if (d->itemView == nullptr) { return; } QItemSelectionModel *selModel = d->itemView->selectionModel(); if (selModel) { selModel->clear(); QModelIndex proxyIndex; foreach (const KFileItem &item, items) { if (!item.isNull()) { const QModelIndex dirIndex = d->dirModel->indexForItem(item); proxyIndex = d->proxyModel->mapFromSource(dirIndex); selModel->select(proxyIndex, QItemSelectionModel::Select); } } if (proxyIndex.isValid()) { selModel->setCurrentIndex(proxyIndex, QItemSelectionModel::NoUpdate); } } } QString KDirOperator::makeCompletion(const QString &string) { if (string.isEmpty()) { d->itemView->selectionModel()->clear(); return QString(); } prepareCompletionObjects(); return d->completion.makeCompletion(string); } QString KDirOperator::makeDirCompletion(const QString &string) { if (string.isEmpty()) { d->itemView->selectionModel()->clear(); return QString(); } prepareCompletionObjects(); return d->dirCompletion.makeCompletion(string); } void KDirOperator::prepareCompletionObjects() { if (d->itemView == nullptr) { return; } if (d->completeListDirty) { // create the list of all possible completions const KFileItemList itemList = d->dirLister->items(); foreach (const KFileItem &item, itemList) { d->completion.addItem(item.name()); if (item.isDir()) { d->dirCompletion.addItem(item.name()); } } d->completeListDirty = false; } } void KDirOperator::slotCompletionMatch(const QString &match) { QUrl url(match); if (url.isRelative()) { url = d->currUrl.resolved(url); } setCurrentItem(url); emit completion(match); } void KDirOperator::setupActions() { d->actionCollection = new KActionCollection(this); d->actionCollection->setObjectName(QStringLiteral("KDirOperator::actionCollection")); d->actionMenu = new KActionMenu(i18n("Menu"), this); d->actionCollection->addAction(QStringLiteral("popupMenu"), d->actionMenu); QAction *upAction = d->actionCollection->addAction(KStandardAction::Up, QStringLiteral("up"), this, SLOT(cdUp())); upAction->setText(i18n("Parent Folder")); d->actionCollection->addAction(KStandardAction::Back, QStringLiteral("back"), this, SLOT(back())); d->actionCollection->addAction(KStandardAction::Forward, QStringLiteral("forward"), this, SLOT(forward())); QAction *homeAction = d->actionCollection->addAction(KStandardAction::Home, QStringLiteral("home"), this, SLOT(home())); homeAction->setText(i18n("Home Folder")); QAction *reloadAction = d->actionCollection->addAction(KStandardAction::Redisplay, QStringLiteral("reload"), this, SLOT(rereadDir())); reloadAction->setText(i18n("Reload")); reloadAction->setShortcuts(KStandardShortcut::shortcut(KStandardShortcut::Reload)); QAction *mkdirAction = new QAction(i18n("New Folder..."), this); d->actionCollection->addAction(QStringLiteral("mkdir"), mkdirAction); mkdirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); connect(mkdirAction, SIGNAL(triggered(bool)), this, SLOT(mkdir())); QAction *trash = new QAction(i18n("Move to Trash"), this); d->actionCollection->addAction(QStringLiteral("trash"), trash); trash->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); trash->setShortcut(Qt::Key_Delete); connect(trash, SIGNAL(triggered(bool)), SLOT(trashSelected())); QAction *action = new QAction(i18n("Delete"), this); d->actionCollection->addAction(QStringLiteral("delete"), action); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); action->setShortcut(Qt::SHIFT + Qt::Key_Delete); connect(action, SIGNAL(triggered(bool)), this, SLOT(deleteSelected())); // the sort menu actions KActionMenu *sortMenu = new KActionMenu(i18n("Sorting"), this); d->actionCollection->addAction(QStringLiteral("sorting menu"), sortMenu); KToggleAction *byNameAction = new KToggleAction(i18n("By Name"), this); d->actionCollection->addAction(QStringLiteral("by name"), byNameAction); connect(byNameAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByName())); KToggleAction *bySizeAction = new KToggleAction(i18n("By Size"), this); d->actionCollection->addAction(QStringLiteral("by size"), bySizeAction); connect(bySizeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortBySize())); KToggleAction *byDateAction = new KToggleAction(i18n("By Date"), this); d->actionCollection->addAction(QStringLiteral("by date"), byDateAction); connect(byDateAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByDate())); KToggleAction *byTypeAction = new KToggleAction(i18n("By Type"), this); d->actionCollection->addAction(QStringLiteral("by type"), byTypeAction); connect(byTypeAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortByType())); KToggleAction *descendingAction = new KToggleAction(i18n("Descending"), this); d->actionCollection->addAction(QStringLiteral("descending"), descendingAction); connect(descendingAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotSortReversed(bool))); KToggleAction *dirsFirstAction = new KToggleAction(i18n("Folders First"), this); d->actionCollection->addAction(QStringLiteral("dirs first"), dirsFirstAction); connect(dirsFirstAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotToggleDirsFirst())); QActionGroup *sortGroup = new QActionGroup(this); byNameAction->setActionGroup(sortGroup); bySizeAction->setActionGroup(sortGroup); byDateAction->setActionGroup(sortGroup); byTypeAction->setActionGroup(sortGroup); d->decorationMenu = new KActionMenu(i18n("Icon Position"), this); d->actionCollection->addAction(QStringLiteral("decoration menu"), d->decorationMenu); d->leftAction = new KToggleAction(i18n("Next to File Name"), this); d->actionCollection->addAction(QStringLiteral("decorationAtLeft"), d->leftAction); connect(d->leftAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotChangeDecorationPosition())); KToggleAction *topAction = new KToggleAction(i18n("Above File Name"), this); d->actionCollection->addAction(QStringLiteral("decorationAtTop"), topAction); connect(topAction, SIGNAL(triggered(bool)), this, SLOT(_k_slotChangeDecorationPosition())); d->decorationMenu->addAction(d->leftAction); d->decorationMenu->addAction(topAction); QActionGroup *decorationGroup = new QActionGroup(this); d->leftAction->setActionGroup(decorationGroup); topAction->setActionGroup(decorationGroup); KToggleAction *shortAction = new KToggleAction(i18n("Short View"), this); d->actionCollection->addAction(QStringLiteral("short view"), shortAction); shortAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons"))); connect(shortAction, SIGNAL(triggered()), SLOT(_k_slotSimpleView())); KToggleAction *detailedAction = new KToggleAction(i18n("Detailed View"), this); d->actionCollection->addAction(QStringLiteral("detailed view"), detailedAction); detailedAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); connect(detailedAction, SIGNAL(triggered()), SLOT(_k_slotDetailedView())); KToggleAction *treeAction = new KToggleAction(i18n("Tree View"), this); d->actionCollection->addAction(QStringLiteral("tree view"), treeAction); treeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); connect(treeAction, SIGNAL(triggered()), SLOT(_k_slotTreeView())); KToggleAction *detailedTreeAction = new KToggleAction(i18n("Detailed Tree View"), this); d->actionCollection->addAction(QStringLiteral("detailed tree view"), detailedTreeAction); detailedTreeAction->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); connect(detailedTreeAction, SIGNAL(triggered()), SLOT(_k_slotDetailedTreeView())); QActionGroup *viewGroup = new QActionGroup(this); shortAction->setActionGroup(viewGroup); detailedAction->setActionGroup(viewGroup); treeAction->setActionGroup(viewGroup); detailedTreeAction->setActionGroup(viewGroup); KToggleAction *showHiddenAction = new KToggleAction(i18n("Show Hidden Files"), this); d->actionCollection->addAction(QStringLiteral("show hidden"), showHiddenAction); showHiddenAction->setShortcuts({Qt::ALT + Qt::Key_Period, Qt::CTRL + Qt::Key_H, Qt::Key_F8}); connect(showHiddenAction, SIGNAL(toggled(bool)), SLOT(_k_slotToggleHidden(bool))); KToggleAction *previewAction = new KToggleAction(i18n("Show Aside Preview"), this); d->actionCollection->addAction(QStringLiteral("preview"), previewAction); previewAction->setShortcut(Qt::Key_F11); connect(previewAction, SIGNAL(toggled(bool)), SLOT(_k_togglePreview(bool))); KToggleAction *inlinePreview = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-preview")), i18n("Show Preview"), this); d->actionCollection->addAction(QStringLiteral("inline preview"), inlinePreview); inlinePreview->setShortcut(Qt::Key_F12); connect(inlinePreview, SIGNAL(toggled(bool)), SLOT(_k_toggleInlinePreviews(bool))); QAction *fileManager = new QAction(i18n("Open Containing Folder"), this); d->actionCollection->addAction(QStringLiteral("file manager"), fileManager); fileManager->setIcon(QIcon::fromTheme(QStringLiteral("system-file-manager"))); connect(fileManager, SIGNAL(triggered()), SLOT(_k_slotOpenFileManager())); action = new QAction(i18n("Properties"), this); d->actionCollection->addAction(QStringLiteral("properties"), action); action->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); action->setShortcut(Qt::ALT + Qt::Key_Return); connect(action, SIGNAL(triggered(bool)), this, SLOT(_k_slotProperties())); // the view menu actions KActionMenu *viewMenu = new KActionMenu(i18n("&View"), this); d->actionCollection->addAction(QStringLiteral("view menu"), viewMenu); viewMenu->addAction(shortAction); viewMenu->addAction(detailedAction); // Comment following lines to hide the extra two modes viewMenu->addAction(treeAction); viewMenu->addAction(detailedTreeAction); // TODO: QAbstractItemView does not offer an action collection. Provide // an interface to add a custom action collection. d->newFileMenu = new KNewFileMenu(d->actionCollection, QStringLiteral("new"), this); connect(d->newFileMenu, SIGNAL(directoryCreated(QUrl)), this, SLOT(_k_slotDirectoryCreated(QUrl))); d->actionCollection->addAssociatedWidget(this); foreach (QAction *action, d->actionCollection->actions()) { action->setShortcutContext(Qt::WidgetWithChildrenShortcut); } } void KDirOperator::setupMenu() { setupMenu(SortActions | ViewActions | FileActions); } void KDirOperator::setupMenu(int whichActions) { // first fill the submenus (sort and view) KActionMenu *sortMenu = static_cast(d->actionCollection->action(QStringLiteral("sorting menu"))); sortMenu->menu()->clear(); sortMenu->addAction(d->actionCollection->action(QStringLiteral("by name"))); sortMenu->addAction(d->actionCollection->action(QStringLiteral("by size"))); sortMenu->addAction(d->actionCollection->action(QStringLiteral("by date"))); sortMenu->addAction(d->actionCollection->action(QStringLiteral("by type"))); sortMenu->addSeparator(); sortMenu->addAction(d->actionCollection->action(QStringLiteral("descending"))); sortMenu->addAction(d->actionCollection->action(QStringLiteral("dirs first"))); // now plug everything into the popupmenu d->actionMenu->menu()->clear(); if (whichActions & NavActions) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("up"))); d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("back"))); d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("forward"))); d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("home"))); d->actionMenu->addSeparator(); } if (whichActions & FileActions) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("new"))); if (d->currUrl.isLocalFile() && !(QApplication::keyboardModifiers() & Qt::ShiftModifier)) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("trash"))); } KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KDE")); const bool del = !d->currUrl.isLocalFile() || (QApplication::keyboardModifiers() & Qt::ShiftModifier) || cg.readEntry("ShowDeleteCommand", false); if (del) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("delete"))); } d->actionMenu->addSeparator(); } if (whichActions & SortActions) { d->actionMenu->addAction(sortMenu); if (!(whichActions & ViewActions)) { d->actionMenu->addSeparator(); } } if (whichActions & ViewActions) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("view menu"))); d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("reload"))); d->actionMenu->addSeparator(); } if (whichActions & FileActions) { d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("file manager"))); d->actionMenu->addAction(d->actionCollection->action(QStringLiteral("properties"))); } } void KDirOperator::updateSortActions() { if (KFile::isSortByName(d->sorting)) { d->actionCollection->action(QStringLiteral("by name"))->setChecked(true); } else if (KFile::isSortByDate(d->sorting)) { d->actionCollection->action(QStringLiteral("by date"))->setChecked(true); } else if (KFile::isSortBySize(d->sorting)) { d->actionCollection->action(QStringLiteral("by size"))->setChecked(true); } else if (KFile::isSortByType(d->sorting)) { d->actionCollection->action(QStringLiteral("by type"))->setChecked(true); } d->actionCollection->action(QStringLiteral("descending"))->setChecked(d->sorting & QDir::Reversed); d->actionCollection->action(QStringLiteral("dirs first"))->setChecked(d->sorting & QDir::DirsFirst); } void KDirOperator::updateViewActions() { KFile::FileView fv = static_cast(d->viewKind); //QAction *separateDirs = d->actionCollection->action("separate dirs"); //separateDirs->setChecked(KFile::isSeparateDirs(fv) && // separateDirs->isEnabled()); d->actionCollection->action(QStringLiteral("short view"))->setChecked(KFile::isSimpleView(fv)); d->actionCollection->action(QStringLiteral("detailed view"))->setChecked(KFile::isDetailView(fv)); d->actionCollection->action(QStringLiteral("tree view"))->setChecked(KFile::isTreeView(fv)); d->actionCollection->action(QStringLiteral("detailed tree view"))->setChecked(KFile::isDetailTreeView(fv)); } void KDirOperator::readConfig(const KConfigGroup &configGroup) { d->defaultView = 0; QString viewStyle = configGroup.readEntry("View Style", "Simple"); if (viewStyle == QLatin1String("Detail")) { d->defaultView |= KFile::Detail; } else if (viewStyle == QLatin1String("Tree")) { d->defaultView |= KFile::Tree; } else if (viewStyle == QLatin1String("DetailTree")) { d->defaultView |= KFile::DetailTree; } else { d->defaultView |= KFile::Simple; } //if (configGroup.readEntry(QLatin1String("Separate Directories"), // DefaultMixDirsAndFiles)) { // d->defaultView |= KFile::SeparateDirs; //} if (configGroup.readEntry(QStringLiteral("Show Preview"), false)) { d->defaultView |= KFile::PreviewContents; } d->previewWidth = configGroup.readEntry(QStringLiteral("Preview Width"), 100); if (configGroup.readEntry(QStringLiteral("Show hidden files"), DefaultShowHidden)) { d->actionCollection->action(QStringLiteral("show hidden"))->setChecked(true); d->dirLister->setShowingDotFiles(true); } QDir::SortFlags sorting = QDir::Name; if (configGroup.readEntry(QStringLiteral("Sort directories first"), DefaultDirsFirst)) { sorting |= QDir::DirsFirst; } QString name = QStringLiteral("Name"); QString sortBy = configGroup.readEntry(QStringLiteral("Sort by"), name); if (sortBy == name) { sorting |= QDir::Name; } else if (sortBy == QLatin1String("Size")) { sorting |= QDir::Size; } else if (sortBy == QLatin1String("Date")) { sorting |= QDir::Time; } else if (sortBy == QLatin1String("Type")) { sorting |= QDir::Type; } if (configGroup.readEntry(QStringLiteral("Sort reversed"), DefaultSortReversed)) { sorting |= QDir::Reversed; } d->updateSorting(sorting); if (d->inlinePreviewState == Private::NotForced) { d->showPreviews = configGroup.readEntry(QStringLiteral("Show Inline Previews"), true); d->showPreviewsConfigEntry = d->showPreviews; } QStyleOptionViewItem::Position pos = (QStyleOptionViewItem::Position) configGroup.readEntry(QStringLiteral("Decoration position"), (int) QStyleOptionViewItem::Left); setDecorationPosition(pos); } void KDirOperator::writeConfig(KConfigGroup &configGroup) { QString sortBy = QStringLiteral("Name"); if (KFile::isSortBySize(d->sorting)) { sortBy = QStringLiteral("Size"); } else if (KFile::isSortByDate(d->sorting)) { sortBy = QStringLiteral("Date"); } else if (KFile::isSortByType(d->sorting)) { sortBy = QStringLiteral("Type"); } configGroup.writeEntry(QStringLiteral("Sort by"), sortBy); configGroup.writeEntry(QStringLiteral("Sort reversed"), d->actionCollection->action(QStringLiteral("descending"))->isChecked()); configGroup.writeEntry(QStringLiteral("Sort directories first"), d->actionCollection->action(QStringLiteral("dirs first"))->isChecked()); // don't save the preview when an application specific preview is in use. bool appSpecificPreview = false; if (d->preview) { KFileMetaPreview *tmp = dynamic_cast(d->preview); appSpecificPreview = (tmp == nullptr); } if (!appSpecificPreview) { KToggleAction *previewAction = static_cast(d->actionCollection->action(QStringLiteral("preview"))); if (previewAction->isEnabled()) { bool hasPreview = previewAction->isChecked(); configGroup.writeEntry(QStringLiteral("Show Preview"), hasPreview); if (hasPreview) { // remember the width of the preview widget QList sizes = d->splitter->sizes(); Q_ASSERT(sizes.count() == 2); configGroup.writeEntry(QStringLiteral("Preview Width"), sizes[1]); } } } configGroup.writeEntry(QStringLiteral("Show hidden files"), d->actionCollection->action(QStringLiteral("show hidden"))->isChecked()); KFile::FileView fv = static_cast(d->viewKind); QString style; if (KFile::isDetailView(fv)) { style = QStringLiteral("Detail"); } else if (KFile::isSimpleView(fv)) { style = QStringLiteral("Simple"); } else if (KFile::isTreeView(fv)) { style = QStringLiteral("Tree"); } else if (KFile::isDetailTreeView(fv)) { style = QStringLiteral("DetailTree"); } configGroup.writeEntry(QStringLiteral("View Style"), style); if (d->inlinePreviewState == Private::NotForced) { configGroup.writeEntry(QStringLiteral("Show Inline Previews"), d->showPreviewsConfigEntry); if (qobject_cast(d->itemView)) { configGroup.writeEntry(QStringLiteral("listViewIconSize"), d->iconsZoom); } else { configGroup.writeEntry(QStringLiteral("detailedViewIconSize"), d->iconsZoom); } } configGroup.writeEntry(QStringLiteral("Decoration position"), (int) d->decorationPosition); } void KDirOperator::resizeEvent(QResizeEvent *) { // resize the splitter and assure that the width of // the preview widget is restored QList sizes = d->splitter->sizes(); const bool hasPreview = (sizes.count() == 2); d->splitter->resize(size()); sizes = d->splitter->sizes(); const bool restorePreviewWidth = hasPreview && (d->previewWidth != sizes[1]); if (restorePreviewWidth) { const int availableWidth = sizes[0] + sizes[1]; sizes[0] = availableWidth - d->previewWidth; sizes[1] = d->previewWidth; d->splitter->setSizes(sizes); } if (hasPreview) { d->previewWidth = sizes[1]; } if (d->progressBar->parent() == this) { // might be reparented into a statusbar d->progressBar->move(2, height() - d->progressBar->height() - 2); } d->updateListViewGrid(); } void KDirOperator::setOnlyDoubleClickSelectsFiles(bool enable) { d->onlyDoubleClickSelectsFiles = enable; // TODO: port to QAbstractItemModel //if (d->itemView != 0) { // d->itemView->setOnlyDoubleClickSelectsFiles(enable); //} } bool KDirOperator::onlyDoubleClickSelectsFiles() const { return d->onlyDoubleClickSelectsFiles; } void KDirOperator::Private::_k_slotStarted() { progressBar->setValue(0); // delay showing the progressbar for one second progressDelayTimer->setSingleShot(true); progressDelayTimer->start(1000); } void KDirOperator::Private::_k_slotShowProgress() { progressBar->raise(); progressBar->show(); QApplication::flush(); } void KDirOperator::Private::_k_slotProgress(int percent) { progressBar->setValue(percent); // we have to redraw this as fast as possible if (progressBar->isVisible()) { QApplication::flush(); } } void KDirOperator::Private::_k_slotIOFinished() { progressDelayTimer->stop(); _k_slotProgress(100); progressBar->hide(); emit parent->finishedLoading(); parent->resetCursor(); if (preview) { preview->clearPreview(); } } void KDirOperator::Private::_k_slotCanceled() { emit parent->finishedLoading(); parent->resetCursor(); } QProgressBar *KDirOperator::progressBar() const { return d->progressBar; } void KDirOperator::clearHistory() { qDeleteAll(d->backStack); d->backStack.clear(); d->actionCollection->action(QStringLiteral("back"))->setEnabled(false); qDeleteAll(d->forwardStack); d->forwardStack.clear(); d->actionCollection->action(QStringLiteral("forward"))->setEnabled(false); } void KDirOperator::setEnableDirHighlighting(bool enable) { d->dirHighlighting = enable; } bool KDirOperator::dirHighlighting() const { return d->dirHighlighting; } bool KDirOperator::dirOnlyMode() const { return dirOnlyMode(d->mode); } bool KDirOperator::dirOnlyMode(uint mode) { return ((mode & KFile::Directory) && (mode & (KFile::File | KFile::Files)) == 0); } void KDirOperator::Private::_k_slotProperties() { if (itemView == nullptr) { return; } const KFileItemList list = parent->selectedItems(); if (!list.isEmpty()) { KPropertiesDialog dialog(list, parent); dialog.exec(); } } void KDirOperator::Private::_k_slotActivated(const QModelIndex &index) { const QModelIndex dirIndex = proxyModel->mapToSource(index); KFileItem item = dirModel->itemForIndex(dirIndex); const Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); if (item.isNull() || (modifiers & Qt::ShiftModifier) || (modifiers & Qt::ControlModifier)) { return; } if (item.isDir()) { parent->selectDir(item); } else { parent->selectFile(item); } } void KDirOperator::Private::_k_slotSelectionChanged() { if (itemView == nullptr) { return; } // In the multiselection mode each selection change is indicated by // emitting a null item. Also when the selection has been cleared, a // null item must be emitted. const bool multiSelectionMode = (itemView->selectionMode() == QAbstractItemView::ExtendedSelection); const bool hasSelection = itemView->selectionModel()->hasSelection(); if (multiSelectionMode || !hasSelection) { KFileItem nullItem; parent->highlightFile(nullItem); } else { KFileItem selectedItem = parent->selectedItems().first(); parent->highlightFile(selectedItem); } } void KDirOperator::Private::_k_openContextMenu(const QPoint &pos) { const QModelIndex proxyIndex = itemView->indexAt(pos); const QModelIndex dirIndex = proxyModel->mapToSource(proxyIndex); KFileItem item = dirModel->itemForIndex(dirIndex); if (item.isNull()) { return; } parent->activatedMenu(item, QCursor::pos()); } void KDirOperator::Private::_k_triggerPreview(const QModelIndex &index) { if ((preview != nullptr && !preview->isHidden()) && index.isValid() && (index.column() == KDirModel::Name)) { const QModelIndex dirIndex = proxyModel->mapToSource(index); const KFileItem item = dirModel->itemForIndex(dirIndex); if (item.isNull()) { return; } if (!item.isDir()) { previewUrl = item.url(); _k_showPreview(); } else { preview->clearPreview(); } } } void KDirOperator::Private::_k_showPreview() { if (preview != nullptr) { preview->showPreview(previewUrl); } } void KDirOperator::Private::_k_slotSplitterMoved(int, int) { const QList sizes = splitter->sizes(); if (sizes.count() == 2) { // remember the width of the preview widget (see KDirOperator::resizeEvent()) previewWidth = sizes[1]; } } void KDirOperator::Private::_k_assureVisibleSelection() { if (itemView == nullptr) { return; } QItemSelectionModel *selModel = itemView->selectionModel(); if (selModel->hasSelection()) { const QModelIndex index = selModel->currentIndex(); itemView->scrollTo(index, QAbstractItemView::EnsureVisible); _k_triggerPreview(index); } } void KDirOperator::Private::_k_synchronizeSortingState(int logicalIndex, Qt::SortOrder order) { QDir::SortFlags newSort = sorting & ~(QDirSortMask | QDir::Reversed); switch (logicalIndex) { case KDirModel::Name: newSort |= QDir::Name; break; case KDirModel::Size: newSort |= QDir::Size; break; case KDirModel::ModifiedTime: newSort |= QDir::Time; break; case KDirModel::Type: newSort |= QDir::Type; break; default: Q_ASSERT(false); } if (order == Qt::DescendingOrder) { newSort |= QDir::Reversed; } updateSorting(newSort); QMetaObject::invokeMethod(parent, "_k_assureVisibleSelection", Qt::QueuedConnection); } void KDirOperator::Private::_k_slotChangeDecorationPosition() { if (!itemView) { return; } QListView *view = qobject_cast(itemView); if (!view) { return; } const bool leftChecked = actionCollection->action(QStringLiteral("decorationAtLeft"))->isChecked(); if (leftChecked) { decorationPosition = QStyleOptionViewItem::Left; view->setFlow(QListView::TopToBottom); } else { decorationPosition = QStyleOptionViewItem::Top; view->setFlow(QListView::LeftToRight); } updateListViewGrid(); itemView->update(); } void KDirOperator::Private::_k_slotExpandToUrl(const QModelIndex &index) { QTreeView *treeView = qobject_cast(itemView); if (!treeView) { return; } const KFileItem item = dirModel->itemForIndex(index); if (item.isNull()) { return; } if (!item.isDir()) { const QModelIndex proxyIndex = proxyModel->mapFromSource(index); QList::Iterator it = itemsToBeSetAsCurrent.begin(); while (it != itemsToBeSetAsCurrent.end()) { const QUrl url = *it; if (url.matches(item.url(), QUrl::StripTrailingSlash) || url.isParentOf(item.url())) { const KFileItem _item = dirLister->findByUrl(url); if (!_item.isNull() && _item.isDir()) { const QModelIndex _index = dirModel->indexForItem(_item); const QModelIndex _proxyIndex = proxyModel->mapFromSource(_index); treeView->expand(_proxyIndex); // if we have expanded the last parent of this item, select it if (item.url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash) == url.adjusted(QUrl::StripTrailingSlash)) { treeView->selectionModel()->select(proxyIndex, QItemSelectionModel::Select); } } it = itemsToBeSetAsCurrent.erase(it); } else { ++it; } } } else if (!itemsToBeSetAsCurrent.contains(item.url())) { itemsToBeSetAsCurrent << item.url(); } } void KDirOperator::Private::_k_slotItemsChanged() { completeListDirty = true; } void KDirOperator::Private::updatePreviewActionState() { if (!itemView) { return; } const QFontMetrics metrics(itemView->viewport()->font()); // hide icon previews when they are too small const bool iconSizeBigEnoughForPreview = itemView->iconSize().height() > metrics.height() * 2; KToggleAction *previewAction = qobject_cast(actionCollection->action(QStringLiteral("inline preview"))); previewAction->setEnabled(iconSizeBigEnoughForPreview); if (iconSizeBigEnoughForPreview) { previewAction->setToolTip(i18n("Show Preview")); } else { previewAction->setToolTip(i18n("Automatically disabled for small icon sizes; increase icon size to see previews")); } calledFromUpdatePreviewActionState = true; previewAction->setChecked(iconSizeBigEnoughForPreview && showPreviewsConfigEntry); calledFromUpdatePreviewActionState = false; } void KDirOperator::Private::updateListViewGrid() { if (!itemView) { return; } updatePreviewActionState(); QListView *view = qobject_cast(itemView); if (!view) { return; } const bool leftChecked = actionCollection->action(QStringLiteral("decorationAtLeft"))->isChecked(); if (leftChecked) { view->setGridSize(QSize()); KFileItemDelegate *delegate = qobject_cast(view->itemDelegate()); if (delegate) { delegate->setMaximumSize(QSize()); } } else { const QFontMetrics metrics(itemView->viewport()->font()); const int height = itemView->iconSize().height() + metrics.height() * 2.5; const int minWidth = qMax(height, metrics.height() * 5); const int scrollBarWidth = itemView->verticalScrollBar()->sizeHint().width(); // Subtract 1 px to prevent flickering when resizing the window // For Oxygen a column is missing after showing the dialog without resizing it, // therefore subtract 4 more (scaled) pixels const int viewPortWidth = itemView->contentsRect().width() - scrollBarWidth - 1 - 4 * itemView->devicePixelRatioF(); const int itemsInRow = qMax(1, viewPortWidth / minWidth); const int remainingWidth = viewPortWidth - (minWidth * itemsInRow); const int width = minWidth + (remainingWidth / itemsInRow); const QSize itemSize(width, height); view->setGridSize(itemSize); KFileItemDelegate *delegate = qobject_cast(view->itemDelegate()); if (delegate) { delegate->setMaximumSize(itemSize); } } } int KDirOperator::Private::iconSizeForViewType(QAbstractItemView *itemView) const { if (!itemView || !configGroup) { return 0; } if (qobject_cast(itemView)) { return configGroup->readEntry("listViewIconSize", 0); } else { return configGroup->readEntry("detailedViewIconSize", 0); } } void KDirOperator::setViewConfig(KConfigGroup &configGroup) { delete d->configGroup; d->configGroup = new KConfigGroup(configGroup); } KConfigGroup *KDirOperator::viewConfigGroup() const { return d->configGroup; } void KDirOperator::setShowHiddenFiles(bool s) { d->actionCollection->action(QStringLiteral("show hidden"))->setChecked(s); } bool KDirOperator::showHiddenFiles() const { return d->actionCollection->action(QStringLiteral("show hidden"))->isChecked(); } QStyleOptionViewItem::Position KDirOperator::decorationPosition() const { return d->decorationPosition; } void KDirOperator::setDecorationPosition(QStyleOptionViewItem::Position position) { d->decorationPosition = position; const bool decorationAtLeft = d->decorationPosition == QStyleOptionViewItem::Left; d->actionCollection->action(QStringLiteral("decorationAtLeft"))->setChecked(decorationAtLeft); d->actionCollection->action(QStringLiteral("decorationAtTop"))->setChecked(!decorationAtLeft); } bool KDirOperator::Private::isReadable(const QUrl &url) { if (!url.isLocalFile()) { return true; // what else can we say? } return QDir(url.toLocalFile()).isReadable(); } void KDirOperator::Private::_k_slotDirectoryCreated(const QUrl &url) { parent->setUrl(url, true); } void KDirOperator::setSupportedSchemes(const QStringList &schemes) { d->supportedSchemes = schemes; rereadDir(); } QStringList KDirOperator::supportedSchemes() const { return d->supportedSchemes; } #include "moc_kdiroperator.cpp" #include "kdiroperator.moc" diff --git a/src/widgets/kurifilter.cpp b/src/widgets/kurifilter.cpp index 106b8bf0..da9107ca 100644 --- a/src/widgets/kurifilter.cpp +++ b/src/widgets/kurifilter.cpp @@ -1,694 +1,694 @@ /* This file is part of the KDE libraries * Copyright (C) 2000 Yves Arrouye * Copyright (C) 2000,2010 Dawit Alemayehu * * 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 "kurifilter.h" #include "hostinfo.h" #include #include #include #include #include #include #include #include typedef QList KUriFilterPluginList; typedef QMap SearchProviderMap; static QString lookupIconNameFor(const QUrl &url, KUriFilterData::UriTypes type) { QString iconName; switch (type) { case KUriFilterData::NetProtocol: iconName = KIO::iconNameForUrl(url); break; case KUriFilterData::Executable: { QString exeName = url.path(); exeName = exeName.mid(exeName.lastIndexOf('/') + 1); // strip path if given KService::Ptr service = KService::serviceByDesktopName(exeName); if (service && service->icon() != QLatin1String("unknown")) { iconName = service->icon(); } // Try to find an icon with the same name as the binary (useful for non-kde apps) // Use iconPath rather than loadIcon() as the latter uses QPixmap (not threadsafe) else if (!KIconLoader::global()->iconPath(exeName, KIconLoader::NoGroup, true).isNull()) { iconName = exeName; } else // not found, use default { iconName = QStringLiteral("system-run"); } break; } case KUriFilterData::Help: { iconName = QStringLiteral("khelpcenter"); break; } case KUriFilterData::Shell: { iconName = QStringLiteral("konsole"); break; } case KUriFilterData::Error: case KUriFilterData::Blocked: { iconName = QStringLiteral("error"); break; } default: break; } return iconName; } class Q_DECL_HIDDEN KUriFilterSearchProvider::KUriFilterSearchProviderPrivate { public: KUriFilterSearchProviderPrivate() {} KUriFilterSearchProviderPrivate(const KUriFilterSearchProviderPrivate &other) : desktopEntryName(other.desktopEntryName), iconName(other.iconName), name(other.name), keys(other.keys) {} QString desktopEntryName; QString iconName; QString name; QStringList keys; }; KUriFilterSearchProvider::KUriFilterSearchProvider() : d(new KUriFilterSearchProvider::KUriFilterSearchProviderPrivate) { } KUriFilterSearchProvider::KUriFilterSearchProvider(const KUriFilterSearchProvider &other) : d(new KUriFilterSearchProvider::KUriFilterSearchProviderPrivate(*(other.d))) { } KUriFilterSearchProvider::~KUriFilterSearchProvider() { delete d; } QString KUriFilterSearchProvider::desktopEntryName() const { return d->desktopEntryName; } QString KUriFilterSearchProvider::iconName() const { return d->iconName; } QString KUriFilterSearchProvider::name() const { return d->name; } QStringList KUriFilterSearchProvider::keys() const { return d->keys; } QString KUriFilterSearchProvider::defaultKey() const { if (d->keys.isEmpty()) { return QString(); } return d->keys.first(); } KUriFilterSearchProvider &KUriFilterSearchProvider::operator=(const KUriFilterSearchProvider &other) { d->desktopEntryName = other.d->desktopEntryName; d->iconName = other.d->iconName; d->keys = other.d->keys; d->name = other.d->name; return *this; } void KUriFilterSearchProvider::setDesktopEntryName(const QString &desktopEntryName) { d->desktopEntryName = desktopEntryName; } void KUriFilterSearchProvider::setIconName(const QString &iconName) { d->iconName = iconName; } void KUriFilterSearchProvider::setName(const QString &name) { d->name = name; } void KUriFilterSearchProvider::setKeys(const QStringList &keys) { d->keys = keys; } class KUriFilterDataPrivate { public: explicit KUriFilterDataPrivate(const QUrl &u, const QString &typedUrl) : checkForExecs(true), wasModified(true), uriType(KUriFilterData::Unknown), searchFilterOptions(KUriFilterData::SearchFilterOptionNone), - url(u), + url(u.adjusted(QUrl::NormalizePathSegments)), typedString(typedUrl) { } ~KUriFilterDataPrivate() { } void setData(const QUrl &u, const QString &typedUrl) { checkForExecs = true; wasModified = true; uriType = KUriFilterData::Unknown; searchFilterOptions = KUriFilterData::SearchFilterOptionNone; - url = u; + url = u.adjusted(QUrl::NormalizePathSegments); typedString = typedUrl; errMsg.clear(); iconName.clear(); absPath.clear(); args.clear(); searchTerm.clear(); searchProvider.clear(); searchTermSeparator = QChar(); alternateDefaultSearchProvider.clear(); alternateSearchProviders.clear(); searchProviderMap.clear(); defaultUrlScheme.clear(); } KUriFilterDataPrivate(KUriFilterDataPrivate *data) { wasModified = data->wasModified; checkForExecs = data->checkForExecs; uriType = data->uriType; searchFilterOptions = data->searchFilterOptions; url = data->url; typedString = data->typedString; errMsg = data->errMsg; iconName = data->iconName; absPath = data->absPath; args = data->args; searchTerm = data->searchTerm; searchTermSeparator = data->searchTermSeparator; searchProvider = data->searchProvider; alternateDefaultSearchProvider = data->alternateDefaultSearchProvider; alternateSearchProviders = data->alternateSearchProviders; searchProviderMap = data->searchProviderMap; defaultUrlScheme = data->defaultUrlScheme; } bool checkForExecs; bool wasModified; KUriFilterData::UriTypes uriType; KUriFilterData::SearchFilterOptions searchFilterOptions; QUrl url; QString typedString; QString errMsg; QString iconName; QString absPath; QString args; QString searchTerm; QString searchProvider; QString alternateDefaultSearchProvider; QString defaultUrlScheme; QChar searchTermSeparator; QStringList alternateSearchProviders; QStringList searchProviderList; SearchProviderMap searchProviderMap; }; KUriFilterData::KUriFilterData() : d(new KUriFilterDataPrivate(QUrl(), QString())) { } KUriFilterData::KUriFilterData(const QUrl &url) : d(new KUriFilterDataPrivate(url, url.toString())) { } KUriFilterData::KUriFilterData(const QString &url) : d(new KUriFilterDataPrivate(QUrl::fromUserInput(url), url)) { } KUriFilterData::KUriFilterData(const KUriFilterData &other) : d(new KUriFilterDataPrivate(other.d)) { } KUriFilterData::~KUriFilterData() { delete d; } QUrl KUriFilterData::uri() const { return d->url; } QString KUriFilterData::errorMsg() const { return d->errMsg; } KUriFilterData::UriTypes KUriFilterData::uriType() const { return d->uriType; } QString KUriFilterData::absolutePath() const { return d->absPath; } bool KUriFilterData::hasAbsolutePath() const { return !d->absPath.isEmpty(); } QString KUriFilterData::argsAndOptions() const { return d->args; } bool KUriFilterData::hasArgsAndOptions() const { return !d->args.isEmpty(); } bool KUriFilterData::checkForExecutables() const { return d->checkForExecs; } QString KUriFilterData::typedString() const { return d->typedString; } QString KUriFilterData::searchTerm() const { return d->searchTerm; } QChar KUriFilterData::searchTermSeparator() const { return d->searchTermSeparator; } QString KUriFilterData::searchProvider() const { return d->searchProvider; } QStringList KUriFilterData::preferredSearchProviders() const { return d->searchProviderList; } KUriFilterSearchProvider KUriFilterData::queryForSearchProvider(const QString &provider) const { const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider); if (searchProvider) { return *(searchProvider); } return KUriFilterSearchProvider(); } QString KUriFilterData::queryForPreferredSearchProvider(const QString &provider) const { const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider); if (searchProvider) { return (searchProvider->defaultKey() % searchTermSeparator() % searchTerm()); } return QString(); } QStringList KUriFilterData::allQueriesForSearchProvider(const QString &provider) const { const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider); if (searchProvider) { return searchProvider->keys(); } return QStringList(); } QString KUriFilterData::iconNameForPreferredSearchProvider(const QString &provider) const { const KUriFilterSearchProvider *searchProvider = d->searchProviderMap.value(provider); if (searchProvider) { return searchProvider->iconName(); } return QString(); } QStringList KUriFilterData::alternateSearchProviders() const { return d->alternateSearchProviders; } QString KUriFilterData::alternateDefaultSearchProvider() const { return d->alternateDefaultSearchProvider; } QString KUriFilterData::defaultUrlScheme() const { return d->defaultUrlScheme; } KUriFilterData::SearchFilterOptions KUriFilterData::searchFilteringOptions() const { return d->searchFilterOptions; } QString KUriFilterData::iconName() { if (d->wasModified) { d->iconName = lookupIconNameFor(d->url, d->uriType); d->wasModified = false; } return d->iconName; } void KUriFilterData::setData(const QUrl &url) { d->setData(url, url.toString()); } void KUriFilterData::setData(const QString &url) { d->setData(QUrl(url), url); } bool KUriFilterData::setAbsolutePath(const QString &absPath) { // Since a malformed URL could possibly be a relative // URL we tag it as a possible local resource... if ((d->url.scheme().isEmpty() || d->url.isLocalFile())) { d->absPath = absPath; return true; } return false; } void KUriFilterData::setCheckForExecutables(bool check) { d->checkForExecs = check; } void KUriFilterData::setAlternateSearchProviders(const QStringList &providers) { d->alternateSearchProviders = providers; } void KUriFilterData::setAlternateDefaultSearchProvider(const QString &provider) { d->alternateDefaultSearchProvider = provider; } void KUriFilterData::setDefaultUrlScheme(const QString &scheme) { d->defaultUrlScheme = scheme; } void KUriFilterData::setSearchFilteringOptions(SearchFilterOptions options) { d->searchFilterOptions = options; } KUriFilterData &KUriFilterData::operator=(const QUrl &url) { d->setData(url, url.toString()); return *this; } KUriFilterData &KUriFilterData::operator=(const QString &url) { d->setData(QUrl(url), url); return *this; } /************************* KUriFilterPlugin ******************************/ KUriFilterPlugin::KUriFilterPlugin(const QString &name, QObject *parent) : QObject(parent), d(nullptr) { setObjectName(name); } // KF6 TODO //KUriFilterPlugin::~KUriFilterPlugin() //{ //} KCModule *KUriFilterPlugin::configModule(QWidget *, const char *) const { return nullptr; } QString KUriFilterPlugin::configName() const { return objectName(); } void KUriFilterPlugin::setFilteredUri(KUriFilterData &data, const QUrl &uri) const { - data.d->url = uri; + data.d->url = uri.adjusted(QUrl::NormalizePathSegments); data.d->wasModified = true; //qDebug() << "Got filtered to:" << uri; } void KUriFilterPlugin::setErrorMsg(KUriFilterData &data, const QString &errmsg) const { data.d->errMsg = errmsg; } void KUriFilterPlugin::setUriType(KUriFilterData &data, KUriFilterData::UriTypes type) const { data.d->uriType = type; data.d->wasModified = true; } void KUriFilterPlugin::setArguments(KUriFilterData &data, const QString &args) const { data.d->args = args; } void KUriFilterPlugin::setSearchProvider(KUriFilterData &data, const QString &provider, const QString &term, const QChar &separator) const { data.d->searchProvider = provider; data.d->searchTerm = term; data.d->searchTermSeparator = separator; } void KUriFilterPlugin::setSearchProviders(KUriFilterData &data, const QList &providers) const { Q_FOREACH (KUriFilterSearchProvider *searchProvider, providers) { data.d->searchProviderList << searchProvider->name(); data.d->searchProviderMap.insert(searchProvider->name(), searchProvider); } } QString KUriFilterPlugin::iconNameFor(const QUrl &url, KUriFilterData::UriTypes type) const { return lookupIconNameFor(url, type); } QHostInfo KUriFilterPlugin::resolveName(const QString &hostname, unsigned long timeout) const { return KIO::HostInfo::lookupHost(hostname, timeout); } /******************************* KUriFilter ******************************/ class KUriFilterPrivate { public: KUriFilterPrivate() {} ~KUriFilterPrivate() { qDeleteAll(pluginList); pluginList.clear(); } QVector pluginList; }; class KUriFilterSingleton { public: KUriFilter instance; }; Q_GLOBAL_STATIC(KUriFilterSingleton, m_self) KUriFilter *KUriFilter::self() { return &m_self()->instance; } KUriFilter::KUriFilter() : d(new KUriFilterPrivate()) { loadPlugins(); } KUriFilter::~KUriFilter() { delete d; } bool KUriFilter::filterUri(KUriFilterData &data, const QStringList &filters) { bool filtered = false; for (KUriFilterPlugin *plugin : qAsConst(d->pluginList)) { // If no specific filters were requested, iterate through all the plugins. // Otherwise, only use available filters. if (filters.isEmpty() || filters.contains(plugin->objectName())) { if (plugin->filterUri(data)) { filtered = true; } } } return filtered; } bool KUriFilter::filterUri(QUrl &uri, const QStringList &filters) { KUriFilterData data(uri); bool filtered = filterUri(data, filters); if (filtered) { uri = data.uri(); } return filtered; } bool KUriFilter::filterUri(QString &uri, const QStringList &filters) { KUriFilterData data(uri); bool filtered = filterUri(data, filters); if (filtered) { uri = data.uri().toString(); } return filtered; } QUrl KUriFilter::filteredUri(const QUrl &uri, const QStringList &filters) { KUriFilterData data(uri); filterUri(data, filters); return data.uri(); } QString KUriFilter::filteredUri(const QString &uri, const QStringList &filters) { KUriFilterData data(uri); filterUri(data, filters); return data.uri().toString(); } #ifndef KIOWIDGETS_NO_DEPRECATED bool KUriFilter::filterSearchUri(KUriFilterData &data) { return filterSearchUri(data, (NormalTextFilter | WebShortcutFilter)); } #endif bool KUriFilter::filterSearchUri(KUriFilterData &data, SearchFilterTypes types) { QStringList filters; if (types & WebShortcutFilter) { filters << QStringLiteral("kurisearchfilter"); } if (types & NormalTextFilter) { filters << QStringLiteral("kuriikwsfilter"); } return filterUri(data, filters); } QStringList KUriFilter::pluginNames() const { QStringList res; res.reserve(d->pluginList.size()); std::transform(d->pluginList.constBegin(), d->pluginList.constEnd(), std::back_inserter(res), [](const KUriFilterPlugin *plugin) { return plugin->objectName(); }); return res; } void KUriFilter::loadPlugins() { QVector plugins = KPluginLoader::findPlugins("kf5/urifilters"); // Sort the plugins by order of priority std::sort(plugins.begin(), plugins.end(), [](const KPluginMetaData &a, const KPluginMetaData &b) { return a.rawData().value("X-KDE-InitialPreference").toInt() > b.rawData().value("X-KDE-InitialPreference").toInt(); }); QStringList pluginNames; pluginNames.reserve(plugins.count()); for (const KPluginMetaData &pluginMetaData : plugins) { const QString fileName = pluginMetaData.fileName().section(QLatin1Char('/'), -1); if (!pluginNames.contains(fileName)) { pluginNames << fileName; KPluginFactory *factory = qobject_cast(pluginMetaData.instantiate()); if (factory) { KUriFilterPlugin *plugin = factory->create(nullptr); if (plugin) { d->pluginList << plugin; } } } } }