#include <KLocalizedString>
#include <KUrlMimeData>
+#ifndef QT_NO_ACCESSIBILITY
+#include <QAccessible>
+#endif
#include <QElapsedTimer>
#include <QIcon>
#include <QMimeData>
return false;
}
- m_itemData[index]->values = currentValues;
if (changedRoles.contains("text")) {
QUrl url = m_itemData[index]->item.url();
+ m_items.remove(url);
url = url.adjusted(QUrl::RemoveFilename);
url.setPath(url.path() + currentValues["text"].toString());
m_itemData[index]->item.setUrl(url);
+ m_items.insert(url, index);
+
+ if (!changedRoles.contains("url")) {
+ changedRoles.insert("url");
+ currentValues["url"] = url;
+ }
}
+ m_itemData[index]->values = currentValues;
emitItemsChangedAndTriggerResorting(KItemRangeList() << KItemRange(index, 1), changedRoles);
return data;
}
+namespace
+{
+QString removeMarks(const QString &original)
+{
+ const auto normalized = original.normalized(QString::NormalizationForm_D);
+ QString res;
+ for (auto ch : normalized) {
+ if (!ch.isMark()) {
+ res.append(ch);
+ }
+ }
+ return res;
+}
+}
+
int KFileItemModel::indexForKeyboardSearch(const QString &text, int startFromIndex) const
{
+ const auto noMarkText = removeMarks(text);
startFromIndex = qMax(0, startFromIndex);
for (int i = startFromIndex; i < count(); ++i) {
- if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
+ if (removeMarks(fileItem(i).text()).startsWith(noMarkText, Qt::CaseInsensitive)) {
return i;
}
}
for (int i = 0; i < startFromIndex; ++i) {
- if (fileItem(i).text().startsWith(text, Qt::CaseInsensitive)) {
+ if (removeMarks(fileItem(i).text()).startsWith(noMarkText, Qt::CaseInsensitive)) {
return i;
}
}
QMutexLocker collatorLock(s_collatorMutex());
if (m_naturalSorting) {
- return collator.compare(a, b);
+ // Split extension, taking into account it can be empty
+ constexpr QString::SectionFlags flags = QString::SectionSkipEmpty | QString::SectionIncludeLeadingSep;
+
+ // Sort by baseName first
+ const QString aBaseName = a.section('.', 0, 0, flags);
+ const QString bBaseName = b.section('.', 0, 0, flags);
+
+ const int res = collator.compare(aBaseName, bBaseName);
+ if (res != 0 || (aBaseName.length() == a.length() && bBaseName.length() == b.length())) {
+ return res;
+ }
+
+ // sliced() has undefined behavior when pos < 0 or pos > size().
+ Q_ASSERT(aBaseName.length() <= a.length() && aBaseName.length() >= 0);
+ Q_ASSERT(bBaseName.length() <= b.length() && bBaseName.length() >= 0);
+
+ // baseNames were equal, sort by extension
+ return collator.compare(a.sliced(aBaseName.length()), b.sliced(bBaseName.length()));
}
const int result = QString::compare(a, b, collator.caseSensitivity());
"part of the text that should not be formatted as a date",
"'Yesterday' (MMMM, yyyy)");
const QString translatedFormat = format.toString();
- if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ if (const int count = translatedFormat.count(QLatin1Char('\'')); count >= 2 && count % 2 == 0) {
newGroupValue = locale.toString(fileTime, translatedFormat);
newGroupValue = i18nc(
"Can be used to script translation of "
"part of the text that should not be formatted as a date",
"'One Week Ago' (MMMM, yyyy)");
const QString translatedFormat = format.toString();
- if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ if (const int count = translatedFormat.count(QLatin1Char('\'')); count >= 2 && count % 2 == 0) {
newGroupValue = locale.toString(fileTime, translatedFormat);
newGroupValue = i18nc(
"Can be used to script translation of "
"part of the text that should not be formatted as a date",
"'Two Weeks Ago' (MMMM, yyyy)");
const QString translatedFormat = format.toString();
- if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ if (const int count = translatedFormat.count(QLatin1Char('\'')); count >= 2 && count % 2 == 0) {
newGroupValue = locale.toString(fileTime, translatedFormat);
newGroupValue = i18nc(
"Can be used to script translation of "
"part of the text that should not be formatted as a date",
"'Three Weeks Ago' (MMMM, yyyy)");
const QString translatedFormat = format.toString();
- if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ if (const int count = translatedFormat.count(QLatin1Char('\'')); count >= 2 && count % 2 == 0) {
newGroupValue = locale.toString(fileTime, translatedFormat);
newGroupValue = i18nc(
"Can be used to script translation of "
"part of the text that should not be formatted as a date",
"'Earlier on' MMMM, yyyy");
const QString translatedFormat = format.toString();
- if (translatedFormat.count(QLatin1Char('\'')) == 2) {
+ if (const int count = translatedFormat.count(QLatin1Char('\'')); count >= 2 && count % 2 == 0) {
newGroupValue = locale.toString(fileTime, translatedFormat);
newGroupValue = i18nc(
"Can be used to script translation of "
{ "releaseYear", ReleaseYearRole, kli18nc("@label", "Release Year"), kli18nc("@label", "Audio"), KLazyLocalizedString(), true, true },
{ "aspectRatio", AspectRatioRole, kli18nc("@label", "Aspect Ratio"), kli18nc("@label", "Video"), KLazyLocalizedString(), true, true },
{ "frameRate", FrameRateRole, kli18nc("@label", "Frame Rate"), kli18nc("@label", "Video"), KLazyLocalizedString(), true, true },
+ { "duration", DurationRole, kli18nc("@label", "Duration"), kli18nc("@label", "Video"), KLazyLocalizedString(), true, true },
{ "path", PathRole, kli18nc("@label", "Path"), kli18nc("@label", "Other"), KLazyLocalizedString(), false, false },
{ "extension", ExtensionRole, kli18nc("@label", "File Extension"), kli18nc("@label", "Other"), KLazyLocalizedString(), false, false },
{ "deletiontime", DeletionTimeRole, kli18nc("@label", "Deletion Time"), kli18nc("@label", "Other"), KLazyLocalizedString(), false, false },