LinuxForum.Fans
Тема

Вернуть привычную сортировку имен файлов в Dolphin

Автор: urfin 8 сообщений 2645 просмотров
#350974

Как вернуть привычную сортировку имен файлов в dolphin? В 13.1 сначала идут русские названия файлов, а потом английские. Хотелось бы наоборот.

#350977

А если LC_COLLATE задать как C? Насколько помню/знаю, порядок сортировки задается этой переменной окружения. Но я с ней не экспериментировал...

#350978

yars, спасибо, попробую, может получится.

UPD. Попробовал. Не помогает. Один черт - цифры, русские, английские. Интересно, кто и с какого перепугу порядок сортировки изменил?

#350981

urfin, а она по умолчанию уже не установлена в С? Если установлена, то это либо в настройках Дельфина нужно искать, либо патчик какой-нибудь на него откатывать. Даже и не знаю, куда еще думать.

#350983

yars, Нет. И дельфин не причем, это где-то в самом KDE надо копаться. Вот какой компонент в кедах отвечает за сортировку? Знал бы, так в исходниках бы изменил, да пересобрал.

#351306

urfin,

text
Настроить Dolphin - Главное - Поведение - снять галку с "Правильная сортировка по номерам"

з.ы. добавил как совет №18

#351332

verg, вельми премного благодарен. Однако, все же что сломано в кедах релиза 13.1, ибо в 12.3 при "Правильной сортировке по номерам" все было ОК.

#351389

из интереса немного покопался в исходниках... Итак, собака зарыта между dolphn'ом, kdelibs, qtcore и icu (библиотека для работы с юникодом).

и так, по порядку:
1. dolphin
Здесь всё просто:

text
m_naturalSorting ? KStringHandler::naturalCompare(a, b, Qt::CaseSensitive)
                            : QString::compare(a, b, Qt::CaseSensitive);

т.е. если галочка стоит, то используется умная функция KStringHandler::naturalCompare() из kdelibs, а если нет, то обычный ламповый QString::compare(), который просто сравнивает строки посимвольно... (и, кстати говоря, обладает мелкими недочётами... например, для русского алфавита, ставит ё в конец)

2. kdelibs

kdecore/text/kstringhandler.cpp:302-451
text
int KStringHandler::naturalCompare(const QString &_a, const QString &_b, Qt::CaseSensitivity caseSensitivity)
{
    // This method chops the input a and b into pieces of
    // digits and non-digits (a1.05 becomes a | 1 | . | 05)
    // and compares these pieces of a and b to each other
    // (first with first, second with second, ...).
    //
    // This is based on the natural sort order code code by Martin Pool
    // http://sourcefrog.net/projects/natsort/
    // Martin Pool agreed to license this under LGPL or GPL.

// FIXME: Using toLower() to implement case insensitive comparison is // sub-optimal, but is needed because we compare strings with // localeAwareCompare(), which does not know about case sensitivity. // A task has been filled for this in Qt Task Tracker with ID 205990. // http://trolltech.com/developer/task-tracker/index_html?method=entry&id=205990 QString a; QString b; if (caseSensitivity == Qt::CaseSensitive) { a = _a; b = _b; } else { a = _a.toLower(); b = _b.toLower(); }

const QChar* currA = a.unicode(); // iterator over a const QChar* currB = b.unicode(); // iterator over b

if (currA == currB) { return 0; }

while (!currA->isNull() && !currB->isNull()) { const QChar* begSeqA = currA; // beginning of a new character sequence of a const QChar* begSeqB = currB; if (currA->unicode() == QChar::ObjectReplacementCharacter) { return 1; }

if (currB->unicode() == QChar::ObjectReplacementCharacter) { return -1; }

if (currA->unicode() == QChar::ReplacementCharacter) { return 1; }

if (currB->unicode() == QChar::ReplacementCharacter) { return -1; }

// find sequence of characters ending at the first non-character while (!currA->isNull() && !currA->isDigit() && !currA->isPunct() && !currA->isSpace()) { ++currA; }

while (!currB->isNull() && !currB->isDigit() && !currB->isPunct() && !currB->isSpace()) { ++currB; }

// compare these sequences const QStringRef& subA(a.midRef(begSeqA - a.unicode(), currA - begSeqA)); const QStringRef& subB(b.midRef(begSeqB - b.unicode(), currB - begSeqB)); const int cmp = QStringRef::localeAwareCompare(subA, subB); if (cmp != 0) { return cmp < 0 ? -1 : +1; }

if (currA->isNull() || currB->isNull()) { break; }

// find sequence of characters ending at the first non-character while ((currA->isPunct() || currA->isSpace()) && (currB->isPunct() || currB->isSpace())) { if (*currA != *currB) { return (*currA < *currB) ? -1 : +1; } ++currA; ++currB; if (currA->isNull() || currB->isNull()) { break; } }

// now some digits follow... if ((*currA == QLatin1Char('0')) || (*currB == QLatin1Char('0'))) { // one digit-sequence starts with 0 -> assume we are in a fraction part // do left aligned comparison (numbers are considered left aligned) while (1) { if (!currA->isDigit() && !currB->isDigit()) { break; } else if (!currA->isDigit()) { return +1; } else if (!currB->isDigit()) { return -1; } else if (*currA < *currB) { return -1; } else if (*currA > *currB) { return + 1; } ++currA; ++currB; } } else { // No digit-sequence starts with 0 -> assume we are looking at some integer // do right aligned comparison. // // The longest run of digits wins. That aside, the greatest // value wins, but we can't know that it will until we've scanned // both numbers to know that they have the same magnitude.

bool isFirstRun = true; int weight = 0; while (1) { if (!currA->isDigit() && !currB->isDigit()) { if (weight != 0) { return weight; } break; } else if (!currA->isDigit()) { if (isFirstRun) { return *currA < *currB ? -1 : +1; } else { return -1; } } else if (!currB->isDigit()) { if (isFirstRun) { return *currA < *currB ? -1 : +1; } else { return +1; } } else if ((*currA < *currB) && (weight == 0)) { weight = -1; } else if ((*currA > *currB) && (weight == 0)) { weight = + 1; } ++currA; ++currB; isFirstRun = false; } } }

if (currA->isNull() && currB->isNull()) { return 0; }

return currA->isNull() ? -1 : + 1; }

KStringHandler::naturalCompare() — основное назначение этой замечательной функции это как раз «человеческая» сортировка по-номерам. Но в добавок ко всему она использует в качестве бек-энда для сравнения строк использует QStringRef::localeAwareCompare(); (что в общем правильно).

3. QtCore
Здесь интерес представляет QString::localeAwareCompare(). Вообще говоря, её реализация зависит от системы, но для unix — это strcoll() (стандартная функция libc) и относительно недавно добавленная icu (а именно ucol_strcoll()). Соответствующий коммит. Ориентировочно, он должен был войти 4.8-лохматый релиз...

4. icu
вот эта штука как раз и ставит кириллицу на первое место... ни какими переменными повлиять на её решение мне не удалось... из каких источников она берёт порядок сортировки я тоже не нашёл... но, честно говоря, я не сильно и пытался... копать разные версии я тоже не стал...

5. Вместо вывода.
Проблема эта Обще-Qt-шная. В частности наблюдается и в clementine. Виноват ли в этом icu или нет вопрос ИМХО пока открытый.
Средств борьбы я вижу три:

  • собрать Qt без icu
  • смириться и забыть,
  • околачиваться и промывать мозги людям на багзиллах...

TODO: поискать баги... уже должны быть открыты...



В качестве бонуса небольшая тестовая программка, которая показывает как разные функции из разных библиотек по-разному сортируют строки...

strcmper.cpp
text
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unicode/ucol.h>
#include <QtCore/QString>
#include <QtCore/QLocale>
#include <KStringHandler>

typedef int (*StrCmpFunc)(const char *s1, const char *s2);

static int strcmp_wrapper(const void *p1, const void *p2, void* cmp_func);

void compare_func(const char **strs, size_t strn, const char *label, StrCmpFunc cmp) { int i; qsort_r(strs, strn, sizeof(char*), strcmp_wrapper, (void *)cmp); printf("%s:", label); for( i=0; i<strn; i++) { printf(" %s",strs[i]); } putchar('\n'); }

static int strcmp_wrapper(const void *p1, const void *p2, void* cmp_func) { StrCmpFunc cmp = (StrCmpFunc) cmp_func; return cmp(* (char * const *) p1, * (char * const *) p2); }

int ucol_strcollUTF8_wrapper(const char *s1, const char *s2) { UErrorCode icuStatus = U_ZERO_ERROR; UCollator *icuCollator = ucol_open("ru-RU", &icuStatus); int rv; if(!icuCollator) { fprintf(stderr, "ucol_open failed: %d(%s)\n", icuStatus, u_errorName(icuStatus)); exit(EXIT_FAILURE); }

rv = ucol_strcollUTF8(icuCollator, s1, strlen(s1), s2, strlen(s2), &icuStatus); ucol_close(icuCollator);

return rv; }

int QString_compare_wrapper(const char *s1, const char *s2) { return QString::compare(QString::fromUtf8(s1), QString::fromUtf8(s2)); }

int QString_localeAwareCompare_wrapper(const char *s1, const char *s2) { return QString::localeAwareCompare(QString::fromUtf8(s1), QString::fromUtf8(s2)); }

int KStringHandler_naturalCompare_wrapper(const char *s1, const char *s2) { return KStringHandler::naturalCompare(QString::fromUtf8(s1), QString::fromUtf8(s2)); }

int main (int argc, char **argv) { char cmp_sign; if (argc < 3) { fprintf(stderr, "Usage: %s <str1> <str2> ...\n", argv[0]); return EXIT_FAILURE; }

compare_func((const char **)argv+1, argc - 1, "strcmp", strcmp); compare_func((const char **)argv+1, argc - 1, "strcoll", strcoll); compare_func((const char **)argv+1, argc - 1, "ucol_strcoll", ucol_strcollUTF8_wrapper);

QLocale locale; // we should initialize some qt global objects compare_func((const char **)argv+1, argc - 1, "QString::compare", QString_compare_wrapper); compare_func((const char **)argv+1, argc - 1, "QString::localeAwareCompare", QString_localeAwareCompare_wrapper); compare_func((const char **)argv+1, argc - 1, "KStringHandler::naturalCompare", KStringHandler_naturalCompare_wrapper); return 0; }

[console]
alexander@goblin /tmp $ g++ -I/usr/include/qt4 -I/usr/include/KDE -L/usr/lib64/qt4 -lQtCore -licui18n -lkdecore strcmper.cpp -g # сборка; пути могут отличаться
alexander@goblin /tmp $ ./a.out ё й я q w s 12 2
strcmp: 12 2 q s w й я ё
strcoll: 12 2 q s w ё й я
ucol_strcoll: 12 2 ё й я q s w
QString::compare: 12 2 q s w й я ё
QString::localeAwareCompare: 12 2 ё й я q s w
KStringHandler::naturalCompare: 2 12 ё й я q s w
[/console]

Войдите или зарегистрируйтесь, чтобы ответить.