|
|
|
@ -119,111 +119,6 @@ Directories::Directories() : Gtk::ListViewText(1) { |
|
|
|
|
|
|
|
|
|
|
|
tree_store->set_sort_column(column_record.name, Gtk::SortType::SORT_ASCENDING); |
|
|
|
tree_store->set_sort_column(column_record.name, Gtk::SortType::SORT_ASCENDING); |
|
|
|
tree_store->set_sort_func(column_record.name, [this](const Gtk::TreeModel::iterator &it1, const Gtk::TreeModel::iterator &it2) { |
|
|
|
tree_store->set_sort_func(column_record.name, [this](const Gtk::TreeModel::iterator &it1, const Gtk::TreeModel::iterator &it2) { |
|
|
|
/// Natural comparison supporting UTF-8 and locale
|
|
|
|
|
|
|
|
struct Natural { |
|
|
|
|
|
|
|
static bool is_digit(char chr) { |
|
|
|
|
|
|
|
return chr >= '0' && chr <= '9'; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int compare_characters(size_t &i1, size_t &i2, const std::string &s1, const std::string &s2) { |
|
|
|
|
|
|
|
ScopeGuard scope_guard{[&i1, &i2] { |
|
|
|
|
|
|
|
++i1; |
|
|
|
|
|
|
|
++i2; |
|
|
|
|
|
|
|
}}; |
|
|
|
|
|
|
|
auto c1 = static_cast<unsigned char>(s1[i1]); |
|
|
|
|
|
|
|
auto c2 = static_cast<unsigned char>(s2[i2]); |
|
|
|
|
|
|
|
if(c1 < 0b10000000 && c2 < 0b10000000) { // Both characters are ascii
|
|
|
|
|
|
|
|
auto at = std::tolower(s1[i1]); |
|
|
|
|
|
|
|
auto bt = std::tolower(s2[i2]); |
|
|
|
|
|
|
|
if(at < bt) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
else if(at == bt) |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Glib::ustring u1; |
|
|
|
|
|
|
|
if(c1 >= 0b11110000) |
|
|
|
|
|
|
|
u1 = s1.substr(i1, 4); |
|
|
|
|
|
|
|
else if(c1 >= 0b11100000) |
|
|
|
|
|
|
|
u1 = s1.substr(i1, 3); |
|
|
|
|
|
|
|
else if(c1 >= 0b11000000) |
|
|
|
|
|
|
|
u1 = s1.substr(i1, 2); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
u1 = s1[i1]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Glib::ustring u2; |
|
|
|
|
|
|
|
if(c2 >= 0b11110000) |
|
|
|
|
|
|
|
u2 = s2.substr(i2, 4); |
|
|
|
|
|
|
|
else if(c2 >= 0b11100000) |
|
|
|
|
|
|
|
u2 = s2.substr(i2, 3); |
|
|
|
|
|
|
|
else if(c2 >= 0b11000000) |
|
|
|
|
|
|
|
u2 = s2.substr(i2, 2); |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
u2 = s2[i2]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
i1 += u1.bytes() - 1; |
|
|
|
|
|
|
|
i2 += u2.bytes() - 1; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
u1 = u1.lowercase(); |
|
|
|
|
|
|
|
u2 = u2.lowercase(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(u1 < u2) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
else if(u1 == u2) |
|
|
|
|
|
|
|
return 0; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int compare_numbers(size_t &i1, size_t &i2, const std::string &s1, const std::string &s2) { |
|
|
|
|
|
|
|
int result = 0; |
|
|
|
|
|
|
|
while(true) { |
|
|
|
|
|
|
|
if(i1 >= s1.size() || !is_digit(s1[i1])) { |
|
|
|
|
|
|
|
if(i2 >= s2.size() || !is_digit(s2[i2])) // a and b has equal number of digits
|
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
return -1; // a has fewer digits
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(i2 >= s2.size() || !is_digit(s2[i2])) |
|
|
|
|
|
|
|
return 1; // b has fewer digits
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(result == 0) { |
|
|
|
|
|
|
|
if(s1[i1] < s2[i2]) |
|
|
|
|
|
|
|
result = -1; |
|
|
|
|
|
|
|
if(s1[i1] > s2[i2]) |
|
|
|
|
|
|
|
result = 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
++i1; |
|
|
|
|
|
|
|
++i2; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int compare(const std::string &s1, const std::string &s2) { |
|
|
|
|
|
|
|
size_t i1 = 0; |
|
|
|
|
|
|
|
size_t i2 = 0; |
|
|
|
|
|
|
|
while(i1 < s1.size() && i2 < s2.size()) { |
|
|
|
|
|
|
|
if(is_digit(s1[i1]) && !is_digit(s2[i2])) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
if(!is_digit(s1[i1]) && is_digit(s2[i2])) |
|
|
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
if(!is_digit(s1[i1]) && !is_digit(s2[i2])) { |
|
|
|
|
|
|
|
auto result = compare_characters(i1, i2, s1, s2); |
|
|
|
|
|
|
|
if(result != 0) |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
else { |
|
|
|
|
|
|
|
auto result = compare_numbers(i1, i2, s1, s2); |
|
|
|
|
|
|
|
if(result != 0) |
|
|
|
|
|
|
|
return result; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if(i1 >= s1.size()) |
|
|
|
|
|
|
|
return -1; |
|
|
|
|
|
|
|
return 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto name1 = it1->get_value(column_record.name); |
|
|
|
auto name1 = it1->get_value(column_record.name); |
|
|
|
auto name2 = it2->get_value(column_record.name); |
|
|
|
auto name2 = it2->get_value(column_record.name); |
|
|
|
if(name1.empty()) |
|
|
|
if(name1.empty()) |
|
|
|
|