Skip to content

Commit 0585258

Browse files
muesli4lukefromdc
authored andcommitted
Add sort criterion by reversed extension segments
The basenames of files are split by dots and then starting from the end each segment is compared to find a sort order. Example: bar.tar.bz2 foo.tar.bz2 a.bar.gz x.tar.gz z.tar.gz test.tex A heuristic determines what extension segments are part of the extension. There is probably no perfect solution but there are much less false positives. As a result the sorting is more intuitive and the displayed column in the list view is better readable and displays extensions more accurately. In addition a bug related to the default sort criteria in the preferences has been fixed.
1 parent 68ace00 commit 0585258

File tree

8 files changed

+238
-1
lines changed

8 files changed

+238
-1
lines changed

libcaja-private/caja-column-utilities.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,13 @@ get_builtin_columns (void)
139139
"description", _("The location of the file."),
140140
NULL));
141141

142+
columns = g_list_append (columns,
143+
g_object_new (CAJA_TYPE_COLUMN,
144+
"name", "extension",
145+
"attribute", "extension",
146+
"label", _("Extension"),
147+
"description", _("The extension of the file."),
148+
NULL));
142149
return columns;
143150
}
144151

libcaja-private/caja-file.c

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
#include <libxml/parser.h>
6767
#include <pwd.h>
6868
#include <stdlib.h>
69+
#include <ctype.h>
6970
#include <sys/time.h>
7071
#include <time.h>
7172
#include <unistd.h>
@@ -99,6 +100,9 @@
99100

100101
#define METADATA_ID_IS_LIST_MASK (1<<31)
101102

103+
#define SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH 3
104+
#define SORT_BY_EXTENSION_MAX_SEGMENTS 3
105+
102106
typedef enum {
103107
SHOW_HIDDEN = 1 << 0,
104108
} FilterOptions;
@@ -126,6 +130,7 @@ static GQuark attribute_name_q,
126130
attribute_accessed_date_q,
127131
attribute_date_accessed_q,
128132
attribute_emblems_q,
133+
attribute_extension_q,
129134
attribute_mime_type_q,
130135
attribute_size_detail_q,
131136
attribute_size_on_disk_detail_q,
@@ -3160,6 +3165,186 @@ compare_by_full_path (CajaFile *file_1, CajaFile *file_2)
31603165
return compare_by_display_name (file_1, file_2);
31613166
}
31623167

3168+
/* prev_extension_segment:
3169+
* @basename The basename of a file
3170+
* @rem_chars A pointer to the amount of remaining characters
3171+
*
3172+
* Finds the next segment delimiter to the left. A starting character of '.' is
3173+
* set to '\0'.
3174+
*
3175+
* Return value: The start of the previous segment (right of the dot) or
3176+
* basename if there are none remaining.
3177+
*/
3178+
static char *
3179+
prev_extension_segment (char *basename, int *rem_chars)
3180+
{
3181+
if (*basename == '.') {
3182+
*basename = 0;
3183+
basename--;
3184+
(*rem_chars)--;
3185+
}
3186+
3187+
while (*rem_chars > 0 && *basename != '.') {
3188+
(*rem_chars)--;
3189+
basename--;
3190+
}
3191+
3192+
return basename + 1;
3193+
}
3194+
3195+
/* is_valid_extension_segment:
3196+
* @segment Part of a modifiable zero-terminated string
3197+
* @segment_index The index of the current segment
3198+
*
3199+
* Uses a heuristic to identify valid file extensions.
3200+
*
3201+
* Return value: Whether the segment is part of the file extension.
3202+
*/
3203+
static gboolean
3204+
is_valid_extension_segment (const char *segment, int segment_index)
3205+
{
3206+
gboolean result;
3207+
gboolean has_letters;
3208+
char c;
3209+
int char_offset;
3210+
switch (segment_index) {
3211+
case 0:
3212+
/* extremely long segments are probably not part of the extension */
3213+
result = strlen (segment) < 20;
3214+
break;
3215+
default:
3216+
has_letters = FALSE;
3217+
char_offset = 0;
3218+
while (TRUE) {
3219+
c = *(segment + char_offset);
3220+
if (c == '\0') {
3221+
result = has_letters;
3222+
break;
3223+
}
3224+
/* allow digits if there are also letters */
3225+
else if (isalpha (c)) {
3226+
has_letters = TRUE;
3227+
}
3228+
/* fail if it is neither digit nor letter */
3229+
else if (!isdigit (c)) {
3230+
result = FALSE;
3231+
break;
3232+
}
3233+
3234+
if (char_offset >= SORT_BY_EXTENSION_FOLLOWING_MAX_LENGTH) {
3235+
result = FALSE;
3236+
break;
3237+
}
3238+
char_offset++;
3239+
}
3240+
}
3241+
return result;
3242+
}
3243+
3244+
static int
3245+
compare_by_extension_segments (CajaFile *file_1, CajaFile *file_2)
3246+
{
3247+
char *name_1, *name_2;
3248+
char *segment_1, *segment_2;
3249+
int compare;
3250+
int rem_chars_1, rem_chars_2;
3251+
gboolean done_1, done_2;
3252+
gboolean is_directory_1, is_directory_2;
3253+
int segment_index;
3254+
3255+
3256+
/* Directories do not have an extension */
3257+
is_directory_1 = caja_file_is_directory (file_1);
3258+
is_directory_2 = caja_file_is_directory (file_2);
3259+
3260+
if (is_directory_1 && is_directory_2) {
3261+
return 0;
3262+
} else if (is_directory_1) {
3263+
return -1;
3264+
} else if (is_directory_2) {
3265+
return 1;
3266+
}
3267+
3268+
name_1 = caja_file_get_display_name (file_1);
3269+
name_2 = caja_file_get_display_name (file_2);
3270+
rem_chars_1 = strlen (name_1);
3271+
rem_chars_2 = strlen (name_2);
3272+
3273+
/* Point to one after the zero character */
3274+
segment_1 = name_1 + rem_chars_1 + 1;
3275+
segment_2 = name_2 + rem_chars_2 + 1;
3276+
3277+
segment_index = 0;
3278+
do {
3279+
segment_1 = prev_extension_segment (segment_1 - 1, &rem_chars_1);
3280+
segment_2 = prev_extension_segment (segment_2 - 1, &rem_chars_2);
3281+
3282+
done_1 = rem_chars_1 <= 0 || !is_valid_extension_segment (segment_1, segment_index);
3283+
done_2 = rem_chars_2 <= 0 || !is_valid_extension_segment (segment_2, segment_index);
3284+
if (done_1 && !done_2) {
3285+
compare = -1;
3286+
break;
3287+
}
3288+
else if (!done_1 && done_2) {
3289+
compare = 1;
3290+
break;
3291+
}
3292+
else if (done_1 && done_2) {
3293+
compare = 0;
3294+
break;
3295+
}
3296+
3297+
segment_index++;
3298+
if (segment_index > SORT_BY_EXTENSION_MAX_SEGMENTS - 1) {
3299+
break;
3300+
}
3301+
compare = strcmp (segment_1, segment_2);
3302+
} while (compare == 0);
3303+
3304+
g_free (name_1);
3305+
g_free (name_2);
3306+
3307+
return compare;
3308+
}
3309+
3310+
static gchar *
3311+
caja_file_get_extension_as_string (CajaFile *file)
3312+
{
3313+
char *name;
3314+
int rem_chars;
3315+
int segment_index;
3316+
char *segment;
3317+
char *right_segment;
3318+
char *result;
3319+
if (!caja_file_is_directory (file)) {
3320+
name = caja_file_get_display_name (file);
3321+
rem_chars = strlen (name);
3322+
segment = prev_extension_segment (name + rem_chars, &rem_chars);
3323+
3324+
if (rem_chars > 0 && is_valid_extension_segment (segment, 0)) {
3325+
segment_index = 1;
3326+
do {
3327+
right_segment = segment;
3328+
segment = prev_extension_segment (segment - 1, &rem_chars);
3329+
if (rem_chars > 0 && is_valid_extension_segment (segment, segment_index)) {
3330+
/* remove zero-termination of segment */
3331+
*(right_segment - 1) = '.';
3332+
}
3333+
else {
3334+
break;
3335+
}
3336+
3337+
segment_index++;
3338+
} while (segment_index < SORT_BY_EXTENSION_MAX_SEGMENTS + 1);
3339+
result = g_strdup (right_segment);
3340+
g_free (name);
3341+
return result;
3342+
}
3343+
g_free (name);
3344+
}
3345+
return g_strdup ("");
3346+
}
3347+
31633348
static int
31643349
caja_file_compare_for_sort_internal (CajaFile *file_1,
31653350
CajaFile *file_2,
@@ -3286,6 +3471,12 @@ caja_file_compare_for_sort (CajaFile *file_1,
32863471
result = compare_by_full_path (file_1, file_2);
32873472
}
32883473
break;
3474+
case CAJA_FILE_SORT_BY_EXTENSION:
3475+
result = compare_by_extension_segments (file_1, file_2);
3476+
if (result == 0) {
3477+
result = compare_by_full_path (file_1, file_2);
3478+
}
3479+
break;
32893480
default:
32903481
g_return_val_if_reached (0);
32913482
}
@@ -3354,6 +3545,11 @@ caja_file_compare_for_sort_by_attribute_q (CajaFile *file_1,
33543545
CAJA_FILE_SORT_BY_EMBLEMS,
33553546
directories_first,
33563547
reversed);
3548+
} else if (attribute == attribute_extension_q) {
3549+
return caja_file_compare_for_sort (file_1, file_2,
3550+
CAJA_FILE_SORT_BY_EXTENSION,
3551+
directories_first,
3552+
reversed);
33573553
}
33583554

33593555
/* it is a normal attribute, compare by strings */
@@ -6308,6 +6504,9 @@ caja_file_get_string_attribute_q (CajaFile *file, GQuark attribute_q)
63086504
return caja_file_get_date_as_string (file,
63096505
CAJA_DATE_TYPE_PERMISSIONS_CHANGED);
63106506
}
6507+
if (attribute_q == attribute_extension_q) {
6508+
return caja_file_get_extension_as_string (file);
6509+
}
63116510
if (attribute_q == attribute_permissions_q) {
63126511
return caja_file_get_permissions_as_string (file);
63136512
}
@@ -8332,6 +8531,7 @@ caja_file_class_init (CajaFileClass *class)
83328531
attribute_accessed_date_q = g_quark_from_static_string ("accessed_date");
83338532
attribute_date_accessed_q = g_quark_from_static_string ("date_accessed");
83348533
attribute_emblems_q = g_quark_from_static_string ("emblems");
8534+
attribute_extension_q = g_quark_from_static_string ("extension");
83358535
attribute_mime_type_q = g_quark_from_static_string ("mime_type");
83368536
attribute_size_detail_q = g_quark_from_static_string ("size_detail");
83378537
attribute_size_on_disk_detail_q = g_quark_from_static_string ("size_on_disk_detail");

libcaja-private/caja-file.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ typedef enum
6464
CAJA_FILE_SORT_BY_ATIME,
6565
CAJA_FILE_SORT_BY_EMBLEMS,
6666
CAJA_FILE_SORT_BY_TRASHED_TIME,
67-
CAJA_FILE_SORT_BY_SIZE_ON_DISK
67+
CAJA_FILE_SORT_BY_SIZE_ON_DISK,
68+
CAJA_FILE_SORT_BY_EXTENSION
6869
} CajaFileSortType;
6970

7071
typedef enum

libcaja-private/org.mate.caja.gschema.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<value nick="emblems" value="7"/>
4040
<value nick="trash-time" value="8"/>
4141
<value nick="size_on_disk" value="9"/>
42+
<value nick="extension" value="10"/>
4243
</enum>
4344

4445
<enum id="org.mate.caja.ZoomLevel">

src/caja-file-management-properties.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ static const char * const zoom_values[] =
9494
NULL
9595
};
9696

97+
/*
98+
* This array corresponds to the object with id "model2" in
99+
* caja-file-management-properties.ui. It has to positionally match with it.
100+
* The purpose is to map values from a combo box to values of the gsettings
101+
* enum.
102+
*/
97103
static const char * const sort_order_values[] =
98104
{
99105
"name",
@@ -104,6 +110,7 @@ static const char * const sort_order_values[] =
104110
"mtime",
105111
"atime",
106112
"emblems",
113+
"extension",
107114
"trash-time",
108115
NULL
109116
};

src/caja-file-management-properties.ui

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@
8383
<row>
8484
<col id="0" translatable="yes">By Size</col>
8585
</row>
86+
<row>
87+
<col id="0" translatable="yes">By Size on Disk</col>
88+
</row>
8689
<row>
8790
<col id="0" translatable="yes">By Type</col>
8891
</row>
@@ -95,6 +98,9 @@
9598
<row>
9699
<col id="0" translatable="yes">By Emblems</col>
97100
</row>
101+
<row>
102+
<col id="0" translatable="yes">By Extension</col>
103+
</row>
98104
<row>
99105
<col id="0" translatable="yes">By Trashed Date</col>
100106
</row>

src/file-manager/caja-icon-view-ui.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<menuitem name="Sort by Modification Date" action="Sort by Modification Date"/>
1818
<menuitem name="Sort by Emblems" action="Sort by Emblems"/>
1919
<menuitem name="Sort by Trash Time" action="Sort by Trash Time"/>
20+
<menuitem name="Sort by Extension" action="Sort by Extension"/>
2021
</placeholder>
2122
<separator name="Layout separator"/>
2223
<menuitem name="Tighter Layout" action="Tighter Layout"/>
@@ -40,6 +41,7 @@
4041
<menuitem name="Sort by Modification Date" action="Sort by Modification Date"/>
4142
<menuitem name="Sort by Emblems" action="Sort by Emblems"/>
4243
<menuitem name="Sort by Trash Time" action="Sort by Trash Time"/>
44+
<menuitem name="Sort by Extension" action="Sort by Extension"/>
4345
</placeholder>
4446
<separator name="Layout separator"/>
4547
<menuitem name="Tighter Layout" action="Tighter Layout"/>

src/file-manager/fm-icon-view.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,13 @@ static const SortCriterion sort_criteria[] =
169169
"Sort by Trash Time",
170170
N_("by T_rash Time"),
171171
N_("Keep icons sorted by trash time in rows")
172+
},
173+
{
174+
CAJA_FILE_SORT_BY_EXTENSION,
175+
"extension",
176+
"Sort by Extension",
177+
N_("by E_xtension"),
178+
N_("Keep icons sorted by reversed extension segments in rows")
172179
}
173180
};
174181

@@ -1770,6 +1777,12 @@ static const GtkRadioActionEntry arrange_radio_entries[] =
17701777
N_("Keep icons sorted by trash time in rows"),
17711778
CAJA_FILE_SORT_BY_TRASHED_TIME
17721779
},
1780+
{
1781+
"Sort by Extension", NULL,
1782+
N_("By E_xtension"), NULL,
1783+
N_("Keep icons sorted by reverse extension segments in rows"),
1784+
CAJA_FILE_SORT_BY_EXTENSION
1785+
},
17731786
};
17741787

17751788
static void

0 commit comments

Comments
 (0)