1 /*****************************************************************************
2 * sort.c : Playlist sorting functions
3 *****************************************************************************
4 * Copyright (C) 1999-2009 VLC authors and VideoLAN
7 * Authors: Clément Stenac <zorglub@videolan.org>
8 * Ilkka Ollakka <ileoo@videolan.org>
9 * Rémi Duraffort <ivoire@videolan.org>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
29 #include <vlc_common.h>
31 #define VLC_INTERNAL_PLAYLIST_SORT_FUNCTIONS
32 #include "vlc_playlist.h"
33 #include "playlist_internal.h"
36 /* General comparison functions */
38 * Compare two items using their title or name
39 * @param first: the first item
40 * @param second: the second item
41 * @return -1, 0 or 1 like strcmp
43 static inline int meta_strcasecmp_title( const playlist_item_t
*first
,
44 const playlist_item_t
*second
)
47 char *psz_first
= input_item_GetTitleFbName( first
->p_input
);
48 char *psz_second
= input_item_GetTitleFbName( second
->p_input
);
50 if( psz_first
&& psz_second
)
51 i_ret
= strcasecmp( psz_first
, psz_second
);
52 else if( !psz_first
&& psz_second
)
54 else if( psz_first
&& !psz_second
)
65 * Compare two intems according to the given meta type
66 * @param first: the first item
67 * @param second: the second item
68 * @param meta: the meta type to use to sort the items
69 * @param b_integer: true if the meta are integers
70 * @return -1, 0 or 1 like strcmp
72 static inline int meta_sort( const playlist_item_t
*first
,
73 const playlist_item_t
*second
,
74 vlc_meta_type_t meta
, bool b_integer
)
77 char *psz_first
= input_item_GetMeta( first
->p_input
, meta
);
78 char *psz_second
= input_item_GetMeta( second
->p_input
, meta
);
81 if( first
->i_children
== -1 && second
->i_children
>= 0 )
83 else if( first
->i_children
>= 0 && second
->i_children
== -1 )
85 /* Both are nodes, sort by name */
86 else if( first
->i_children
>= 0 && second
->i_children
>= 0 )
87 i_ret
= meta_strcasecmp_title( first
, second
);
89 else if( !psz_first
&& !psz_second
)
91 else if( !psz_first
&& psz_second
)
93 else if( psz_first
&& !psz_second
)
98 i_ret
= atoi( psz_first
) - atoi( psz_second
);
100 i_ret
= strcasecmp( psz_first
, psz_second
);
108 /* Comparison functions */
111 * Return the comparison function appropriate for the SORT_* and ORDER_*
112 * arguments given, or NULL for SORT_RANDOM.
113 * @param i_mode: a SORT_* enum indicating the field to sort on
114 * @param i_type: ORDER_NORMAL or ORDER_REVERSE
115 * @return function pointer, or NULL for SORT_RANDOM or invalid input
117 typedef int (*sortfn_t
)(const void *,const void *);
118 static const sortfn_t sorting_fns
[NUM_SORT_FNS
][2];
119 static inline sortfn_t
find_sorting_fn( unsigned i_mode
, unsigned i_type
)
121 if( i_mode
>=NUM_SORT_FNS
|| i_type
>1 )
123 return sorting_fns
[i_mode
][i_type
];
127 * Sort an array of items recursively
128 * @param i_items: number of items
129 * @param pp_items: the array of items
130 * @param p_sortfn: the sorting function
134 void playlist_ItemArraySort( unsigned i_items
, playlist_item_t
**pp_items
,
139 qsort( pp_items
, i_items
, sizeof( pp_items
[0] ), p_sortfn
);
145 playlist_item_t
*p_temp
;
147 for( i_position
= i_items
- 1; i_position
> 0; i_position
-- )
149 i_new
= ((unsigned)vlc_mrand48()) % (i_position
+1);
150 p_temp
= pp_items
[i_position
];
151 pp_items
[i_position
] = pp_items
[i_new
];
152 pp_items
[i_new
] = p_temp
;
159 * Sort a node recursively.
160 * This function must be entered with the playlist lock !
161 * @param p_playlist the playlist
162 * @param p_node the node to sort
163 * @param p_sortfn the sorting function
164 * @return VLC_SUCCESS on success
166 static int recursiveNodeSort( playlist_t
*p_playlist
, playlist_item_t
*p_node
,
170 playlist_ItemArraySort(p_node
->i_children
,p_node
->pp_children
,p_sortfn
);
171 for( i
= 0 ; i
< p_node
->i_children
; i
++ )
173 if( p_node
->pp_children
[i
]->i_children
!= -1 )
175 recursiveNodeSort( p_playlist
, p_node
->pp_children
[i
], p_sortfn
);
182 * Sort a node recursively.
184 * This function must be entered with the playlist lock !
186 * \param p_playlist the playlist
187 * \param p_node the node to sort
188 * \param i_mode: a SORT_* constant indicating the field to sort on
189 * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
190 * \return VLC_SUCCESS on success
192 int playlist_RecursiveNodeSort( playlist_t
*p_playlist
, playlist_item_t
*p_node
,
193 int i_mode
, int i_type
)
197 /* Ask the playlist to reset as we are changing the order */
198 pl_priv(p_playlist
)->b_reset_currently_playing
= true;
200 /* Do the real job recursively */
201 return recursiveNodeSort(p_playlist
,p_node
,find_sorting_fn(i_mode
,i_type
));
205 /* This is the stuff the sorting functions are made of. The proto_##
206 * functions are wrapped in cmp_a_## and cmp_d_## functions that do
207 * void * to const playlist_item_t * casting and dereferencing and
208 * cmp_d_## inverts the result, too. proto_## are static inline,
209 * cmp_[ad]_## are merely static as they're the target of pointers.
211 * In any case, each SORT_## constant (except SORT_RANDOM) must have
212 * a matching SORTFN( )-declared function here.
215 #define SORTFN( SORT, first, second ) static inline int proto_##SORT \
216 ( const playlist_item_t *first, const playlist_item_t *second )
218 SORTFN( SORT_TRACK_NUMBER
, first
, second
)
220 return meta_sort( first
, second
, vlc_meta_TrackNumber
, true );
223 SORTFN( SORT_DISC_NUMBER
, first
, second
)
225 int i_ret
= meta_sort( first
, second
, vlc_meta_DiscNumber
, true );
226 /* Items came from the same disc: compare the track numbers */
228 i_ret
= proto_SORT_TRACK_NUMBER( first
, second
);
233 SORTFN( SORT_ALBUM
, first
, second
)
235 int i_ret
= meta_sort( first
, second
, vlc_meta_Album
, false );
236 /* Items came from the same album: compare the disc numbers */
238 i_ret
= proto_SORT_DISC_NUMBER( first
, second
);
243 SORTFN( SORT_DATE
, first
, second
)
245 int i_ret
= meta_sort( first
, second
, vlc_meta_Date
, true );
246 /* Items came from the same date: compare the albums */
248 i_ret
= proto_SORT_ALBUM( first
, second
);
253 SORTFN( SORT_ARTIST
, first
, second
)
255 int i_ret
= meta_sort( first
, second
, vlc_meta_Artist
, false );
256 /* Items came from the same artist: compare the dates */
258 i_ret
= proto_SORT_DATE( first
, second
);
263 SORTFN( SORT_DESCRIPTION
, first
, second
)
265 return meta_sort( first
, second
, vlc_meta_Description
, false );
268 SORTFN( SORT_DURATION
, first
, second
)
270 mtime_t time1
= input_item_GetDuration( first
->p_input
);
271 mtime_t time2
= input_item_GetDuration( second
->p_input
);
272 int i_ret
= time1
> time2
? 1 :
273 ( time1
== time2
? 0 : -1 );
277 SORTFN( SORT_GENRE
, first
, second
)
279 return meta_sort( first
, second
, vlc_meta_Genre
, false );
282 SORTFN( SORT_ID
, first
, second
)
284 return first
->i_id
- second
->i_id
;
287 SORTFN( SORT_RATING
, first
, second
)
289 return meta_sort( first
, second
, vlc_meta_Rating
, true );
292 SORTFN( SORT_TITLE
, first
, second
)
294 return meta_strcasecmp_title( first
, second
);
297 SORTFN( SORT_TITLE_NODES_FIRST
, first
, second
)
299 /* If first is a node but not second */
300 if( first
->i_children
== -1 && second
->i_children
>= 0 )
302 /* If second is a node but not first */
303 else if( first
->i_children
>= 0 && second
->i_children
== -1 )
305 /* Both are nodes or both are not nodes */
307 return meta_strcasecmp_title( first
, second
);
310 SORTFN( SORT_TITLE_NUMERIC
, first
, second
)
313 char *psz_first
= input_item_GetTitleFbName( first
->p_input
);
314 char *psz_second
= input_item_GetTitleFbName( second
->p_input
);
316 if( psz_first
&& psz_second
)
317 i_ret
= atoi( psz_first
) - atoi( psz_second
);
318 else if( !psz_first
&& psz_second
)
320 else if( psz_first
&& !psz_second
)
330 SORTFN( SORT_URI
, first
, second
)
333 char *psz_first
= input_item_GetURI( first
->p_input
);
334 char *psz_second
= input_item_GetURI( second
->p_input
);
336 if( psz_first
&& psz_second
)
337 i_ret
= strcasecmp( psz_first
, psz_second
);
338 else if( !psz_first
&& psz_second
)
340 else if( psz_first
&& !psz_second
)
352 /* Generate stubs around the proto_## sorting functions, ascending and
353 * descending both. Preprocessor magic up ahead. Brace yourself.
356 #ifndef VLC_DEFINE_SORT_FUNCTIONS
357 #error Where is VLC_DEFINE_SORT_FUNCTIONS?
361 static int cmp_a_##s(const void *l,const void *r) \
362 { return proto_##s(*(const playlist_item_t *const *)l, \
363 *(const playlist_item_t *const *)r); } \
364 static int cmp_d_##s(const void *l,const void *r) \
365 { return -1*proto_##s(*(const playlist_item_t * const *)l, \
366 *(const playlist_item_t * const *)r); }
368 VLC_DEFINE_SORT_FUNCTIONS
372 /* And populate an array with the addresses */
374 static const sortfn_t sorting_fns
[NUM_SORT_FNS
][2] =
375 #define DEF( a ) { cmp_a_##a, cmp_d_##a },
376 { VLC_DEFINE_SORT_FUNCTIONS
};