* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <vlc_common.h>
+#define VLC_INTERNAL_PLAYLIST_SORT_FUNCTIONS
#include "vlc_playlist.h"
#include "playlist_internal.h"
-static void playlist_ItemArraySort( int i_items, playlist_item_t **pp_items,
- int i_mode, int i_type );
-static int playlist_cmp( const void *, const void * );
-
-/* Comparaison functions */
-static int playlist_cmp_album( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_artist( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_desc( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_duration( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_genre( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_id( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_rating( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_title( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_title_nodes_first( const playlist_item_t *,
- const playlist_item_t *);
-static int playlist_cmp_title_num( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_track_num( const playlist_item_t *, const playlist_item_t *);
-static int playlist_cmp_uri( const playlist_item_t *, const playlist_item_t *);
-
-/* General comaraison functions */
-static inline int meta_strcasecmp_title( const playlist_item_t *,
- const playlist_item_t *);
-static inline int meta_sort( const playlist_item_t *, const playlist_item_t *,
- vlc_meta_type_t, bool );
+/* General comparison functions */
+/**
+ * Compare two items using their title or name
+ * @param first: the first item
+ * @param second: the second item
+ * @return -1, 0 or 1 like strcmp
+ */
+static inline int meta_strcasecmp_title( const playlist_item_t *first,
+ const playlist_item_t *second )
+{
+ int i_ret;
+ char *psz_first = input_item_GetTitleFbName( first->p_input );
+ char *psz_second = input_item_GetTitleFbName( second->p_input );
+ if( psz_first && psz_second )
+ i_ret = strcasecmp( psz_first, psz_second );
+ else if( !psz_first && psz_second )
+ i_ret = 1;
+ else if( psz_first && !psz_second )
+ i_ret = -1;
+ else
+ i_ret = 0;
+ free( psz_first );
+ free( psz_second );
+
+ return i_ret;
+}
/**
- * Sort a node recursively.
- * This function must be entered with the playlist lock !
- * @param p_playlist the playlist
- * @param p_node the node to sort
- * @param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM
- * @param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
- * @return VLC_SUCCESS on success
+ * Compare two intems accoring to the given meta type
+ * @param first: the first item
+ * @param second: the second item
+ * @param meta: the meta type to use to sort the items
+ * @param b_integer: true if the meta are integers
+ * @return -1, 0 or 1 like strcmp
*/
-static int recursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node,
- int i_mode, int i_type )
+static inline int meta_sort( const playlist_item_t *first,
+ const playlist_item_t *second,
+ vlc_meta_type_t meta, bool b_integer )
{
- int i;
- playlist_ItemArraySort( p_node->i_children, p_node->pp_children,
- i_mode, i_type );
- for( i = 0 ; i< p_node->i_children; i++ )
+ int i_ret;
+ char *psz_first = input_item_GetMeta( first->p_input, meta );
+ char *psz_second = input_item_GetMeta( second->p_input, meta );
+
+ /* Nodes go first */
+ if( first->i_children == -1 && second->i_children >= 0 )
+ i_ret = 1;
+ else if( first->i_children >= 0 && second->i_children == -1 )
+ i_ret = -1;
+ /* Both are nodes, sort by name */
+ else if( first->i_children >= 0 && second->i_children >= 0 )
+ i_ret = meta_strcasecmp_title( first, second );
+ /* Both are items */
+ else if( !psz_first && psz_second )
+ i_ret = 1;
+ else if( psz_first && !psz_second )
+ i_ret = -1;
+ /* No meta, sort by name */
+ else if( !psz_first && !psz_second )
+ i_ret = meta_strcasecmp_title( first, second );
+ else
{
- if( p_node->pp_children[i]->i_children != -1 )
- {
- recursiveNodeSort( p_playlist, p_node->pp_children[i],
- i_mode, i_type );
- }
+ if( b_integer )
+ i_ret = atoi( psz_first ) - atoi( psz_second );
+ else
+ i_ret = strcasecmp( psz_first, psz_second );
}
- return VLC_SUCCESS;
+
+ free( psz_first );
+ free( psz_second );
+ return i_ret;
}
+/* Comparison functions */
+
/**
- * Sort a node recursively.
- *
- * This function must be entered with the playlist lock !
- *
- * \param p_playlist the playlist
- * \param p_node the node to sort
- * \param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM
- * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
- * \return VLC_SUCCESS on success
+ * Return the comparison function appropriate for the SORT_* and ORDER_*
+ * arguments given, or NULL for SORT_RANDOM.
+ * @param i_mode: a SORT_* enum indicating the field to sort on
+ * @param i_type: ORDER_NORMAL or ORDER_REVERSE
+ * @return function pointer, or NULL for SORT_RANDOM or invalid input
*/
-int playlist_RecursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node,
- int i_mode, int i_type )
+typedef int (*sortfn_t)(const void *,const void *);
+static const sortfn_t sorting_fns[NUM_SORT_FNS][2];
+static inline sortfn_t find_sorting_fn( unsigned i_mode, unsigned i_type )
{
- /* Ask the playlist to reset as we are changing the order */
- pl_priv(p_playlist)->b_reset_currently_playing = true;
-
- /* Do the real job recursively */
- return recursiveNodeSort( p_playlist, p_node, i_mode, i_type );
+ if( i_mode>=NUM_SORT_FNS || i_type>1 )
+ return 0;
+ return sorting_fns[i_mode][i_type];
}
-
-static int (*sort_function)(const playlist_item_t *, const playlist_item_t *);
-static int sort_order = 1;
-
-
/**
* Sort an array of items recursively
* @param i_items: number of items
* @param pp_items: the array of items
- * @param i_mode: the criterias for the comparisons
- * @param i_type: ORDER_NORMAL or ORDER_REVERSE
+ * @param p_sortfn: the sorting function
* @return nothing
*/
-static void playlist_ItemArraySort( int i_items, playlist_item_t **pp_items,
- int i_mode, int i_type )
+static inline
+void playlist_ItemArraySort( unsigned i_items, playlist_item_t **pp_items,
+ sortfn_t p_sortfn )
{
- int i_position;
- playlist_item_t *p_temp;
-
- /* Random sort */
- if( i_mode == SORT_RANDOM )
+ if( p_sortfn )
{
+ qsort( pp_items, i_items, sizeof( pp_items[0] ), p_sortfn );
+ }
+ else /* Randomise */
+ {
+ int i_position;
+ playlist_item_t *p_temp;
+
for( i_position = 0; i_position < i_items ; i_position++ )
{
int i_new;
pp_items[i_new] = p_temp;
}
}
- else
+}
+
+
+/**
+ * Sort a node recursively.
+ * This function must be entered with the playlist lock !
+ * @param p_playlist the playlist
+ * @param p_node the node to sort
+ * @param p_sortfn the sorting function
+ * @return VLC_SUCCESS on success
+ */
+static int recursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node,
+ sortfn_t p_sortfn )
+{
+ int i;
+ playlist_ItemArraySort(p_node->i_children,p_node->pp_children,p_sortfn);
+ for( i = 0 ; i< p_node->i_children; i++ )
{
- /* Choose the funtion to compare two items */
- switch( i_mode )
+ if( p_node->pp_children[i]->i_children != -1 )
{
- case SORT_ALBUM:
- sort_function = playlist_cmp_album;
- break;
- case SORT_ARTIST:
- sort_function = playlist_cmp_artist;
- break;
- case SORT_DESCRIPTION:
- sort_function = playlist_cmp_desc;
- break;
- case SORT_DURATION:
- sort_function = playlist_cmp_duration;
- break;
- case SORT_GENRE:
- sort_function = playlist_cmp_genre;
- break;
- case SORT_ID:
- sort_function = playlist_cmp_id;
- break;
- case SORT_TITLE:
- sort_function = playlist_cmp_title;
- break;
- case SORT_TITLE_NODES_FIRST:
- sort_function = playlist_cmp_title_nodes_first;
- break;
- case SORT_TITLE_NUMERIC:
- sort_function = playlist_cmp_title_num;
- break;
- case SORT_TRACK_NUMBER:
- sort_function = playlist_cmp_track_num;
- break;
- case SORT_RATING:
- sort_function = playlist_cmp_rating;
- break;
- case SORT_URI:
- sort_function = playlist_cmp_uri;
- break;
- default:
- assert(0);
+ recursiveNodeSort( p_playlist, p_node->pp_children[i], p_sortfn );
}
- sort_order = i_type == ORDER_REVERSE ? -1 : 1;
- qsort( pp_items, i_items, sizeof( pp_items[0] ), playlist_cmp );
}
+ return VLC_SUCCESS;
}
-
/**
- * Wrapper around playlist_cmp_* function
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
+ * Sort a node recursively.
+ *
+ * This function must be entered with the playlist lock !
+ *
+ * \param p_playlist the playlist
+ * \param p_node the node to sort
+ * \param i_mode: a SORT_* constant indicating the field to sort on
+ * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
+ * \return VLC_SUCCESS on success
*/
-static int playlist_cmp( const void *first, const void *second )
+int playlist_RecursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node,
+ int i_mode, int i_type )
{
- if( sort_order == -1 )
- return -1 * sort_function( *(playlist_item_t **)first,
- *(playlist_item_t **)second );
- else
- return sort_function( *(playlist_item_t **)first,
- *(playlist_item_t **)second );
+ /* Ask the playlist to reset as we are changing the order */
+ pl_priv(p_playlist)->b_reset_currently_playing = true;
+
+ /* Do the real job recursively */
+ return recursiveNodeSort(p_playlist,p_node,find_sorting_fn(i_mode,i_type));
}
-/**
- * Compare two items according to the title
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
+/* This is the stuff the sorting functions are made of. The proto_##
+ * functions are wrapped in cmp_a_## and cmp_d_## functions that do
+ * void * to const playlist_item_t * casting and dereferencing and
+ * cmp_d_## inverts the result, too. proto_## are static inline,
+ * cmp_[ad]_## are merely static as they're the target of pointers.
+ *
+ * In any case, each SORT_## constant (except SORT_RANDOM) must have
+ * a matching SORTFN( )-declared function here.
*/
-static int playlist_cmp_album( const playlist_item_t *first,
- const playlist_item_t *second )
+
+#define SORTFN( SORT, first, second ) static inline int proto_##SORT \
+ ( const playlist_item_t *first, const playlist_item_t *second )
+
+SORTFN( SORT_ALBUM, first, second )
{
int i_ret = meta_sort( first, second, vlc_meta_Album, false );
/* Items came from the same album: compare the track numbers */
return i_ret;
}
-
-/**
- * Compare two items according to the artist
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_artist( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_ARTIST, first, second )
{
int i_ret = meta_sort( first, second, vlc_meta_Artist, false );
/* Items came from the same artist: compare the albums */
if( i_ret == 0 )
- i_ret = playlist_cmp_album( first, second );
+ i_ret = proto_SORT_ALBUM( first, second );
return i_ret;
}
-
-/**
- * Compare two items according to the description
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_desc( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_DESCRIPTION, first, second )
{
return meta_sort( first, second, vlc_meta_Description, false );
}
-
-/**
- * Compare two items according to the duration
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_duration( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_DURATION, first, second )
{
return input_item_GetDuration( first->p_input ) -
input_item_GetDuration( second->p_input );
}
-
-/**
- * Compare two items according to the genre
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_genre( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_GENRE, first, second )
{
return meta_sort( first, second, vlc_meta_Genre, false );
}
-
-/**
- * Compare two items according to the ID
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_id( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_ID, first, second )
{
return first->i_id - second->i_id;
}
-
-/**
- * Compare two items according to the rating
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_rating( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_RATING, first, second )
{
return meta_sort( first, second, vlc_meta_Rating, true );
}
-
-/**
- * Compare two items according to the title
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_title( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_TITLE, first, second )
{
return meta_strcasecmp_title( first, second );
}
-
-/**
- * Compare two items according to the title, with the nodes first in the list
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_title_nodes_first( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_TITLE_NODES_FIRST, first, second )
{
/* If first is a node but not second */
if( first->i_children == -1 && second->i_children >= 0 )
return meta_strcasecmp_title( first, second );
}
-
-/**
- * Compare two item according to the title as a numeric value
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_title_num( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_TITLE_NUMERIC, first, second )
{
int i_ret;
char *psz_first = input_item_GetTitleFbName( first->p_input );
return i_ret;
}
-
-/**
- * Compare two item according to the track number
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_track_num( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_TRACK_NUMBER, first, second )
{
return meta_sort( first, second, vlc_meta_TrackNumber, true );
}
-
-/**
- * Compare two item according to the URI
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
- */
-static int playlist_cmp_uri( const playlist_item_t *first,
- const playlist_item_t *second )
+SORTFN( SORT_URI, first, second )
{
int i_ret;
char *psz_first = input_item_GetURI( first->p_input );
return i_ret;
}
+#undef SORTFN
-/**
- * Compare two items using their title or name
- * @param first: the first item
- * @param second: the second item
- * @return -1, 0 or 1 like strcmp
+/* Generate stubs around the proto_## sorting functions, ascending and
+ * descending both. Preprocessor magic up ahead. Brace yourself.
*/
-static inline int meta_strcasecmp_title( const playlist_item_t *first,
- const playlist_item_t *second )
-{
- int i_ret;
- char *psz_first = input_item_GetTitleFbName( first->p_input );
- char *psz_second = input_item_GetTitleFbName( second->p_input );
- if( psz_first && psz_second )
- i_ret = strcasecmp( psz_first, psz_second );
- else if( !psz_first && psz_second )
- i_ret = 1;
- else if( psz_first && !psz_second )
- i_ret = -1;
- else
- i_ret = 0;
- free( psz_first );
- free( psz_second );
+#ifndef VLC_DEFINE_SORT_FUNCTIONS
+#error Where is VLC_DEFINE_SORT_FUNCTIONS?
+#endif
- return i_ret;
-}
+#define DEF( s ) \
+ static int cmp_a_##s(const void *l,const void *r) \
+ { return proto_##s(*(const playlist_item_t *const *)l, \
+ *(const playlist_item_t *const *)r); } \
+ static int cmp_d_##s(const void *l,const void *r) \
+ { return -1*proto_##s(*(const playlist_item_t * const *)l, \
+ *(const playlist_item_t * const *)r); }
+ VLC_DEFINE_SORT_FUNCTIONS
-/**
- * Compare two intems accoring to the given meta type
- * @param first: the first item
- * @param second: the second item
- * @param meta: the meta type to use to sort the items
- * @param b_integer: true if the meta are integers
- * @return -1, 0 or 1 like strcmp
- */
-static inline int meta_sort( const playlist_item_t *first,
- const playlist_item_t *second,
- vlc_meta_type_t meta, bool b_integer )
-{
- int i_ret;
- char *psz_first = input_item_GetMeta( first->p_input, meta );
- char *psz_second = input_item_GetMeta( second->p_input, meta );
+#undef DEF
- /* Nodes go first */
- if( first->i_children == -1 && second->i_children >= 0 )
- i_ret = 1;
- else if( first->i_children >= 0 && second->i_children == -1 )
- i_ret = -1;
- /* Both are nodes, sort by name */
- else if( first->i_children >= 0 && second->i_children >= 0 )
- i_ret = meta_strcasecmp_title( first, second );
- /* Both are items */
- else if( !psz_first && psz_second )
- i_ret = 1;
- else if( psz_first && !psz_second )
- i_ret = -1;
- /* No meta, sort by name */
- else if( !psz_first && !psz_second )
- i_ret = meta_strcasecmp_title( first, second );
- else
- {
- if( b_integer )
- i_ret = atoi( psz_first ) - atoi( psz_second );
- else
- i_ret = strcasecmp( psz_first, psz_second );
- }
+/* And populate an array with the addresses */
- free( psz_first );
- free( psz_second );
- return i_ret;
-}
+static const sortfn_t sorting_fns[NUM_SORT_FNS][2] =
+#define DEF( a ) { cmp_a_##a, cmp_d_##a },
+{ VLC_DEFINE_SORT_FUNCTIONS };
+#undef DEF