]> git.sesse.net Git - vlc/blob - src/playlist/sort.c
Merge branch 'master' of git@git.videolan.org:vlc
[vlc] / src / playlist / sort.c
1 /*****************************************************************************
2  * sort.c : Playlist sorting functions
3  *****************************************************************************
4  * Copyright (C) 1999-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Ilkka Ollakka <ileoo@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <vlc/vlc.h>
29 #include "vlc_playlist.h"
30 #include "playlist_internal.h"
31
32
33 static int playlist_ItemArraySort( playlist_t *p_playlist, int i_items,
34                                    playlist_item_t **pp_items, int i_mode,
35                                    int i_type );
36 static int playlist_cmp(const void *, const void *);
37
38 /**
39  * Sort a node.
40  * This function must be entered with the playlist lock !
41  *
42  * \param p_playlist the playlist
43  * \param p_node the node to sort
44  * \param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM
45  * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
46  * \return VLC_SUCCESS on success
47  */
48 static int playlist_NodeSort( playlist_t * p_playlist , playlist_item_t *p_node,
49                               int i_mode, int i_type )
50 {
51     playlist_ItemArraySort( p_playlist,p_node->i_children,
52                             p_node->pp_children, i_mode, i_type );
53     return VLC_SUCCESS;
54 }
55
56 /**
57  * Sort a node recursively.
58  *
59  * This function must be entered with the playlist lock !
60  *
61  * \param p_playlist the playlist
62  * \param p_node the node to sort
63  * \param i_mode: SORT_ID, SORT_TITLE, SORT_ARTIST, SORT_ALBUM, SORT_RANDOM
64  * \param i_type: ORDER_NORMAL or ORDER_REVERSE (reversed order)
65  * \return VLC_SUCCESS on success
66  */
67 int playlist_RecursiveNodeSort( playlist_t *p_playlist, playlist_item_t *p_node,
68                                 int i_mode, int i_type )
69 {
70     int i;
71     playlist_NodeSort( p_playlist, p_node, i_mode, i_type );
72     for( i = 0 ; i< p_node->i_children; i++ )
73     {
74         if( p_node->pp_children[i]->i_children != -1 )
75         {
76             playlist_RecursiveNodeSort( p_playlist, p_node->pp_children[i],
77                                         i_mode,i_type );
78         }
79     }
80     return VLC_SUCCESS;
81 }
82
83 static int sort_mode = 0;
84 static int sort_type = 0;
85
86 static int playlist_ItemArraySort( playlist_t *p_playlist, int i_items,
87                                    playlist_item_t **pp_items, int i_mode,
88                                    int i_type )
89 {
90     int i_position;
91     playlist_item_t *p_temp;
92     vlc_value_t val;
93     val.b_bool = true;
94     sort_mode = i_mode;
95     sort_type = i_type;
96
97     (void)p_playlist; // a bit surprising we don't need p_playlist!
98
99     if( i_mode == SORT_RANDOM )
100     {
101         for( i_position = 0; i_position < i_items ; i_position ++ )
102         {
103             int i_new;
104
105             if( i_items > 1 )
106                 i_new = rand() % (i_items - 1);
107             else
108                 i_new = 0;
109             p_temp = pp_items[i_position];
110             pp_items[i_position] = pp_items[i_new];
111             pp_items[i_new] = p_temp;
112         }
113
114         return VLC_SUCCESS;
115     }
116     qsort(pp_items,i_items,sizeof(pp_items[0]),playlist_cmp);
117     return VLC_SUCCESS;
118 }
119
120 static int playlist_cmp(const void *first, const void *second)
121 {
122
123 #define META_STRCASECMP_NAME( ) { \
124     char *psz_i = input_item_GetName( (*(playlist_item_t **)first)->p_input ); \
125     char *psz_ismall = input_item_GetName( (*(playlist_item_t **)second)->p_input ); \
126     i_test = strcasecmp( psz_i, psz_ismall ); \
127     free( psz_i ); \
128     free( psz_ismall ); \
129 }
130
131
132 #define DO_META_SORT_ADV( node, integer ) { \
133     char *psz_a = input_item_GetMeta( (*(playlist_item_t **)first)->p_input, vlc_meta_##node ); \
134     char *psz_b = input_item_GetMeta( (*(playlist_item_t **)second)->p_input, vlc_meta_##node ); \
135     /* Nodes go first */ \
136     if( (*(playlist_item_t **)first)->i_children == -1 && (*(playlist_item_t **)second)->i_children >= 0 ) \
137         i_test = 1;\
138     else if( (*(playlist_item_t **)first)->i_children >= 0 &&\
139              (*(playlist_item_t **)second)->i_children == -1 ) \
140        i_test = -1; \
141     /* Both are nodes, sort by name */ \
142     else if( (*(playlist_item_t **)first)->i_children >= 0 && \
143                (*(playlist_item_t **)second)->i_children >= 0 ) \
144     { \
145         META_STRCASECMP_NAME( ) \
146     } \
147     /* Both are items */ \
148     else if( psz_a == NULL && psz_b != NULL ) \
149         i_test = 1; \
150     else if( psz_a != NULL && psz_b == NULL ) \
151         i_test = -1;\
152     /* No meta, sort by name */ \
153     else if( psz_a == NULL && psz_b == NULL ) \
154     { \
155         META_STRCASECMP_NAME( ); \
156     } \
157     else \
158     { \
159         if( !integer ) i_test = strcmp( psz_a, psz_b ); \
160         else           i_test = atoi( psz_a ) - atoi( psz_b ); \
161     } \
162     free( psz_a ); \
163     free( psz_b ); \
164 }
165 #define DO_META_SORT( node ) DO_META_SORT_ADV( node, false )
166
167     int i_test = 0;
168
169     if( sort_mode == SORT_TITLE )
170         {
171             META_STRCASECMP_NAME( );
172         }
173     else if( sort_mode == SORT_TITLE_NUMERIC )
174     {
175         char *psz_i = input_item_GetName( (*(playlist_item_t **)first)->p_input );
176         char *psz_ismall =
177                 input_item_GetName( (*(playlist_item_t **)second)->p_input );
178         i_test = atoi( psz_i ) - atoi( psz_ismall );
179         free( psz_i );
180         free( psz_ismall );
181     }
182     else if( sort_mode == SORT_DURATION )
183     {
184         i_test = input_item_GetDuration( (*(playlist_item_t **)first)->p_input ) -
185                  input_item_GetDuration( (*(playlist_item_t **)second)->p_input );
186     }
187     else if( sort_mode == SORT_ARTIST )
188     {
189         DO_META_SORT( Artist );
190         /* sort by artist, album, tracknumber */
191         if( i_test == 0 )
192             DO_META_SORT( Album );
193         if( i_test == 0 )
194             DO_META_SORT_ADV( TrackNumber, true );
195     }
196     else if( sort_mode == SORT_GENRE )
197     {
198         DO_META_SORT( Genre );
199     }
200     else if( sort_mode == SORT_ALBUM )
201     {
202         DO_META_SORT( Album );
203         /* Sort by tracknumber if albums are the same */
204         if( i_test == 0 )
205             DO_META_SORT_ADV( TrackNumber, true );
206     }
207     else if( sort_mode == SORT_TRACK_NUMBER )
208     {
209         DO_META_SORT_ADV( TrackNumber, true );
210     }
211     else if( sort_mode == SORT_DESCRIPTION )
212     {
213         DO_META_SORT( Description );
214     }
215     else if( sort_mode == SORT_ID )
216     {
217         i_test = (*(playlist_item_t **)first)->i_id - (*(playlist_item_t **)second)->i_id;
218     }
219     else if( sort_mode == SORT_TITLE_NODES_FIRST )
220     {
221         /* Alphabetic sort, all nodes first */
222
223         if( (*(playlist_item_t **)first)->i_children == -1 &&
224             (*(playlist_item_t **)second)->i_children >= 0 )
225         {
226             i_test = 1;
227         }
228         else if( (*(playlist_item_t **)first)->i_children >= 0 &&
229                  (*(playlist_item_t **)second)->i_children == -1 )
230         {
231             i_test = -1;
232         }
233         else
234         {
235             i_test = strcasecmp( (*(playlist_item_t **)first)->p_input->psz_name,
236                                  (*(playlist_item_t **)second)->p_input->psz_name );
237         }
238     }
239
240     if ( sort_type == ORDER_REVERSE ) 
241         i_test = i_test * -1;
242 #undef DO_META_SORT
243 #undef DO_META_SORT_ADV
244
245     return i_test;
246 }