1

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

2

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

Истинный hotplug - это обычная электрическая розетка: воткнул - работает, и никаких драйверов.
Arch Linux/Xfce/Xiaomi Mi Notebook Pro 15.6 | Slackware64-current/Xfce/Lenovo G580

3 (24.11.2013 11:36:43 отредактировано urfin)

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

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

4

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

Истинный hotplug - это обычная электрическая розетка: воткнул - работает, и никаких драйверов.
Arch Linux/Xfce/Xiaomi Mi Notebook Pro 15.6 | Slackware64-current/Xfce/Lenovo G580

5

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

6

urfin,

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


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

7

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

8 (26.11.2013 14:27:34 отредактировано Fat-Zer)

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

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

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
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
#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;
}

 Консоль:

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

95% процентов проблем находятся между клавиатурой и стулом.