]> git.sesse.net Git - vlc/blob - src/playlist/sort.c
playlist: Don't assume that psz_name != NULL as GetName can return NULL and
[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_common.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     if( psz_i != NULL && psz_ismall != NULL ) i_test = strcasecmp( psz_i, psz_ismall ); \
127     else if ( psz_i == NULL && psz_ismall != NULL ) i_test = 1; \
128     else if ( psz_ismall == NULL && psz_i != NULL ) i_test = -1; \
129     else i_test = 0; \
130     free( psz_i ); \
131     free( psz_ismall ); \
132 }
133
134
135 #define DO_META_SORT_ADV( node, integer ) { \
136     char *psz_a = input_item_GetMeta( (*(playlist_item_t **)first)->p_input, vlc_meta_##node ); \
137     char *psz_b = input_item_GetMeta( (*(playlist_item_t **)second)->p_input, vlc_meta_##node ); \
138     /* Nodes go first */ \
139     if( (*(playlist_item_t **)first)->i_children == -1 && (*(playlist_item_t **)second)->i_children >= 0 ) \
140         i_test = 1;\
141     else if( (*(playlist_item_t **)first)->i_children >= 0 &&\
142              (*(playlist_item_t **)second)->i_children == -1 ) \
143        i_test = -1; \
144     /* Both are nodes, sort by name */ \
145     else if( (*(playlist_item_t **)first)->i_children >= 0 && \
146                (*(playlist_item_t **)second)->i_children >= 0 ) \
147     { \
148         META_STRCASECMP_NAME( ) \
149     } \
150     /* Both are items */ \
151     else if( psz_a == NULL && psz_b != NULL ) \
152         i_test = 1; \
153     else if( psz_a != NULL && psz_b == NULL ) \
154         i_test = -1;\
155     /* No meta, sort by name */ \
156     else if( psz_a == NULL && psz_b == NULL ) \
157     { \
158         META_STRCASECMP_NAME( ); \
159     } \
160     else \
161     { \
162         if( !integer ) i_test = strcmp( psz_a, psz_b ); \
163         else           i_test = atoi( psz_a ) - atoi( psz_b ); \
164     } \
165     free( psz_a ); \
166     free( psz_b ); \
167 }
168 #define DO_META_SORT( node ) DO_META_SORT_ADV( node, false )
169
170     int i_test = 0;
171
172     if( sort_mode == SORT_TITLE )
173         {
174             META_STRCASECMP_NAME( );
175         }
176     else if( sort_mode == SORT_TITLE_NUMERIC )
177     {
178         char *psz_i = input_item_GetName( (*(playlist_item_t **)first)->p_input );
179         char *psz_ismall =
180                 input_item_GetName( (*(playlist_item_t **)second)->p_input );
181         i_test = atoi( psz_i ) - atoi( psz_ismall );
182         free( psz_i );
183         free( psz_ismall );
184     }
185     else if( sort_mode == SORT_DURATION )
186     {
187         i_test = input_item_GetDuration( (*(playlist_item_t **)first)->p_input ) -
188                  input_item_GetDuration( (*(playlist_item_t **)second)->p_input );
189     }
190     else if( sort_mode == SORT_ARTIST )
191     {
192         DO_META_SORT( Artist );
193         /* sort by artist, album, tracknumber */
194         if( i_test == 0 )
195             DO_META_SORT( Album );
196         if( i_test == 0 )
197             DO_META_SORT_ADV( TrackNumber, true );
198     }
199     else if( sort_mode == SORT_GENRE )
200     {
201         DO_META_SORT( Genre );
202     }
203     else if( sort_mode == SORT_ALBUM )
204     {
205         DO_META_SORT( Album );
206         /* Sort by tracknumber if albums are the same */
207         if( i_test == 0 )
208             DO_META_SORT_ADV( TrackNumber, true );
209     }
210     else if( sort_mode == SORT_TRACK_NUMBER )
211     {
212         DO_META_SORT_ADV( TrackNumber, true );
213     }
214     else if( sort_mode == SORT_DESCRIPTION )
215     {
216         DO_META_SORT( Description );
217     }
218     else if( sort_mode == SORT_ID )
219     {
220         i_test = (*(playlist_item_t **)first)->i_id - (*(playlist_item_t **)second)->i_id;
221     }
222     else if( sort_mode == SORT_TITLE_NODES_FIRST )
223     {
224         /* Alphabetic sort, all nodes first */
225
226         if( (*(playlist_item_t **)first)->i_children == -1 &&
227             (*(playlist_item_t **)second)->i_children >= 0 )
228         {
229             i_test = 1;
230         }
231         else if( (*(playlist_item_t **)first)->i_children >= 0 &&
232                  (*(playlist_item_t **)second)->i_children == -1 )
233         {
234             i_test = -1;
235         }
236         else
237         {
238             if ( (*(playlist_item_t **)first)->p_input->psz_name != NULL &&
239                  (*(playlist_item_t **)second)->p_input->psz_name != NULL )
240             {
241                 i_test = strcasecmp( (*(playlist_item_t **)first)->p_input->psz_name,
242                                  (*(playlist_item_t **)second)->p_input->psz_name );
243             }
244             else if ( (*(playlist_item_t **)first)->p_input->psz_name != NULL &&
245                  (*(playlist_item_t **)second)->p_input->psz_name == NULL )
246             {
247                 i_test = 1;
248             }
249             else if ( (*(playlist_item_t **)first)->p_input->psz_name == NULL &&
250                  (*(playlist_item_t **)second)->p_input->psz_name != NULL )
251             {
252                 i_test = -1;
253             }
254             else i_test = 0;
255         }
256     }
257     else if( sort_mode == SORT_URI )
258     {
259         char *psz_i = input_item_GetURI( (*(playlist_item_t **)first)->p_input );
260         char *psz_ismall =
261                 input_item_GetURI( (*(playlist_item_t **)second)->p_input );
262         i_test = strcasecmp( psz_i, psz_ismall );
263         free( psz_i );
264         free( psz_ismall );
265     }
266
267     if ( sort_type == ORDER_REVERSE )
268         i_test = i_test * -1;
269 #undef DO_META_SORT
270 #undef DO_META_SORT_ADV
271
272     return i_test;
273 }