49061f76208e8acfe7b395b5aad81d8975eb76fa
[vuplus_xbmc] / xbmc / utils / SortUtils.cpp
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "SortUtils.h"
22 #include "URL.h"
23 #include "Util.h"
24 #include "XBDateTime.h"
25 #include "settings/AdvancedSettings.h"
26 #include "utils/CharsetConverter.h"
27 #include "utils/StdString.h"
28 #include "utils/StringUtils.h"
29 #include "utils/Variant.h"
30
31 using namespace std;
32
33 string ArrayToString(SortAttribute attributes, const CVariant &variant, const string &seperator = " / ")
34 {
35   vector<string> strArray;
36   if (variant.isArray())
37   {
38     for (CVariant::const_iterator_array it = variant.begin_array(); it != variant.end_array(); it++)
39     {
40       if (attributes & SortAttributeIgnoreArticle)
41         strArray.push_back(SortUtils::RemoveArticles(it->asString()));
42       else
43         strArray.push_back(it->asString());
44     }
45
46     return StringUtils::Join(strArray, seperator);
47   }
48   else if (variant.isString())
49   {
50     if (attributes & SortAttributeIgnoreArticle)
51       return SortUtils::RemoveArticles(variant.asString());
52     else
53       return variant.asString();
54   }
55
56   return "";
57 }
58
59 string ByLabel(SortAttribute attributes, const SortItem &values)
60 {
61   if (attributes & SortAttributeIgnoreArticle)
62     return SortUtils::RemoveArticles(values.at(FieldLabel).asString());
63
64   return values.at(FieldLabel).asString();
65 }
66
67 string ByFile(SortAttribute attributes, const SortItem &values)
68 {
69   CURL url(values.at(FieldPath).asString());
70   
71   CStdString label;
72   label.Format("%s %d", url.GetFileNameWithoutPath().c_str(), values.at(FieldStartOffset).asInteger());
73   return label;
74 }
75
76 string ByPath(SortAttribute attributes, const SortItem &values)
77 {
78   CStdString label;
79   label.Format("%s %d", values.at(FieldPath).asString().c_str(), values.at(FieldStartOffset).asInteger());
80   return label;
81 }
82
83 string ByLastPlayed(SortAttribute attributes, const SortItem &values)
84 {
85   CStdString label;
86   label.Format("%s %s", values.at(FieldLastPlayed).asString().c_str(), ByLabel(attributes, values).c_str());
87   return label;
88 }
89
90 string ByPlaycount(SortAttribute attributes, const SortItem &values)
91 {
92   CStdString label;
93   label.Format("%i %s", (int)values.at(FieldPlaycount).asInteger(), ByLabel(attributes, values).c_str());
94   return label;
95 }
96
97 string ByDate(SortAttribute attributes, const SortItem &values)
98 {
99   return values.at(FieldDate).asString() + " " + ByLabel(attributes, values);
100 }
101
102 string ByDateAdded(SortAttribute attributes, const SortItem &values)
103 {
104   CStdString label;
105   label.Format("%s %d", values.at(FieldDateAdded).asString().c_str(), (int)values.at(FieldId).asInteger());
106   return label;
107 }
108
109 string BySize(SortAttribute attributes, const SortItem &values)
110 {
111   CStdString label;
112   label.Format("%"PRId64, values.at(FieldSize).asInteger());
113   return label;
114 }
115
116 string ByDriveType(SortAttribute attributes, const SortItem &values)
117 {
118   CStdString label;
119   label.Format("%d %s", (int)values.at(FieldDriveType).asInteger(), ByLabel(attributes, values).c_str());
120   return label;
121 }
122
123 string ByTitle(SortAttribute attributes, const SortItem &values)
124 {
125   if (attributes & SortAttributeIgnoreArticle)
126     return SortUtils::RemoveArticles(values.at(FieldTitle).asString());
127
128   return values.at(FieldTitle).asString();
129 }
130
131 string ByAlbum(SortAttribute attributes, const SortItem &values)
132 {
133   string album = values.at(FieldAlbum).asString();
134   if (attributes & SortAttributeIgnoreArticle)
135     album = SortUtils::RemoveArticles(album);
136
137   CStdString label;
138   label.Format("%s %s", album, ArrayToString(attributes, values.at(FieldArtist)));
139
140   const CVariant &track = values.at(FieldTrackNumber);
141   if (!track.isNull())
142     label.AppendFormat(" %i", (int)track.asInteger());
143
144   return label;
145 }
146
147 string ByAlbumType(SortAttribute attributes, const SortItem &values)
148 {
149   return values.at(FieldAlbumType).asString() + " " + ByLabel(attributes, values);
150 }
151
152 string ByArtist(SortAttribute attributes, const SortItem &values)
153 {
154   CStdString label = ArrayToString(attributes, values.at(FieldArtist));
155
156   const CVariant &year = values.at(FieldYear);
157   if (g_advancedSettings.m_bMusicLibraryAlbumsSortByArtistThenYear &&
158       !year.isNull())
159     label.AppendFormat(" %i", (int)year.asInteger());
160
161   const CVariant &album = values.at(FieldAlbum);
162   if (!album.isNull())
163     label += " " + SortUtils::RemoveArticles(album.asString());
164
165   const CVariant &track = values.at(FieldTrackNumber);
166   if (!track.isNull())
167     label.AppendFormat(" %i", (int)track.asInteger());
168
169   return label;
170 }
171
172 string ByTrackNumber(SortAttribute attributes, const SortItem &values)
173 {
174   CStdString label;
175   label.Format("%i", (int)values.at(FieldTrackNumber).asInteger());
176   return label;
177 }
178
179 string ByTime(SortAttribute attributes, const SortItem &values)
180 {
181   CStdString label;
182   const CVariant &time = values.at(FieldTime);
183   if (time.isInteger())
184     label.Format("%i", (int)time.asInteger());
185   else
186     label.Format("%s", time.asString());
187   return label;
188 }
189
190 string ByProgramCount(SortAttribute attributes, const SortItem &values)
191 {
192   CStdString label;
193   label.Format("%i", (int)values.at(FieldProgramCount).asInteger());
194   return label;
195 }
196
197 string ByPlaylistOrder(SortAttribute attributes, const SortItem &values)
198 {
199   // TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
200   return ByProgramCount(attributes, values);
201 }
202
203 string ByGenre(SortAttribute attributes, const SortItem &values)
204 {
205   return ArrayToString(attributes, values.at(FieldGenre));
206 }
207
208 string ByCountry(SortAttribute attributes, const SortItem &values)
209 {
210   return ArrayToString(attributes, values.at(FieldCountry));
211 }
212
213 string ByYear(SortAttribute attributes, const SortItem &values)
214 {
215   CStdString label;
216   const CVariant &airDate = values.at(FieldAirDate);
217   if (!airDate.isNull())
218     label = airDate.asString() + " ";
219
220   label.AppendFormat("%i %s", (int)values.at(FieldYear).asInteger(), ByLabel(attributes, values).c_str());
221  
222   return label;
223 }
224
225 string BySortTitle(SortAttribute attributes, const SortItem &values)
226 {
227   string title = values.at(FieldSortTitle).asString();
228   if (title.empty())
229     title = values.at(FieldTitle).asString();
230
231   if (attributes & SortAttributeIgnoreArticle)
232     title = SortUtils::RemoveArticles(title);
233
234   return title;
235 }
236
237 string ByRating(SortAttribute attributes, const SortItem &values)
238 {
239   CStdString label;
240   label.Format("%f %s", values.at(FieldRating).asFloat(), ByLabel(attributes, values).c_str());
241   return label;
242 }
243
244 string ByVotes(SortAttribute attributes, const SortItem &values)
245 {
246   CStdString label;
247   label.Format("%d %s", (int)values.at(FieldVotes).asInteger(), ByLabel(attributes, values).c_str());
248   return label;
249 }
250
251 string ByTop250(SortAttribute attributes, const SortItem &values)
252 {
253   CStdString label;
254   label.Format("%d %s", (int)values.at(FieldTop250).asInteger(), ByLabel(attributes, values).c_str());
255   return label;
256 }
257
258 string ByMPAA(SortAttribute attributes, const SortItem &values)
259 {
260   return values.at(FieldMPAA).asString() + " " + ByLabel(attributes, values);
261 }
262
263 string ByStudio(SortAttribute attributes, const SortItem &values)
264 {
265   return ArrayToString(attributes, values.at(FieldStudio));
266 }
267
268 string ByEpisodeNumber(SortAttribute attributes, const SortItem &values)
269 {
270   // we calculate an offset number based on the episode's
271   // sort season and episode values. in addition
272   // we include specials 'episode' numbers to get proper
273   // sorting of multiple specials in a row. each
274   // of these are given their particular ranges to semi-ensure uniqueness.
275   // theoretical problem: if a show has > 2^15 specials and two of these are placed
276   // after each other they will sort backwards. if a show has > 2^32-1 seasons
277   // or if a season has > 2^16-1 episodes strange things will happen (overflow)
278   uint64_t num;
279   const CVariant &episodeSpecial = values.at(FieldEpisodeNumberSpecialSort);
280   const CVariant &seasonSpecial = values.at(FieldSeasonSpecialSort);
281   if (!episodeSpecial.isNull() && !seasonSpecial.isNull() &&
282      (episodeSpecial.asInteger() > 0 || seasonSpecial.asInteger() > 0))
283     num = ((uint64_t)seasonSpecial.asInteger() << 32) + (episodeSpecial.asInteger() << 16) - ((2 << 15) - values.at(FieldEpisodeNumber).asInteger());
284   else
285     num = ((uint64_t)values.at(FieldSeason).asInteger() << 32) + (values.at(FieldEpisodeNumber).asInteger() << 16);
286
287   std::string title;
288   if (values.find(FieldMediaType) != values.end() && values.at(FieldMediaType).asInteger() == MediaTypeMovie)
289     title = BySortTitle(attributes, values);
290   if (title.empty())
291     title = ByLabel(attributes, values);
292
293   CStdString label;
294   label.Format("%"PRIu64" %s", num, title.c_str());
295   return label;
296 }
297
298 string BySeason(SortAttribute attributes, const SortItem &values)
299 {
300   int season = (int)values.at(FieldSeason).asInteger();
301   const CVariant &specialSeason = values.at(FieldSeasonSpecialSort);
302   if (!specialSeason.isNull())
303     season = (int)specialSeason.asInteger();
304
305   CStdString label;
306   label.Format("%i %s", season, ByLabel(attributes, values).c_str());
307   return label;
308 }
309
310 string ByNumberOfEpisodes(SortAttribute attributes, const SortItem &values)
311 {
312   CStdString label;
313   label.Format("%i %s", (int)values.at(FieldNumberOfEpisodes).asInteger(), ByLabel(attributes, values).c_str());
314   return label;
315 }
316
317 string ByNumberOfWatchedEpisodes(SortAttribute attributes, const SortItem &values)
318 {
319   CStdString label;
320   label.Format("%i %s", (int)values.at(FieldNumberOfWatchedEpisodes).asInteger(), ByLabel(attributes, values).c_str());
321   return label;
322 }
323
324 string ByTvShowStatus(SortAttribute attributes, const SortItem &values)
325 {
326   return values.at(FieldTvShowStatus).asString() + " " + ByLabel(attributes, values);
327 }
328
329 string ByTvShowTitle(SortAttribute attributes, const SortItem &values)
330 {
331   return values.at(FieldTvShowTitle).asString() + " " + ByLabel(attributes, values);
332 }
333
334 string ByProductionCode(SortAttribute attributes, const SortItem &values)
335 {
336   return values.at(FieldProductionCode).asString();
337 }
338
339 string ByVideoResolution(SortAttribute attributes, const SortItem &values)
340 {
341   CStdString label;
342   label.Format("%i %s", (int)values.at(FieldVideoResolution).asInteger(), ByLabel(attributes, values).c_str());
343   return label;
344 }
345
346 string ByVideoCodec(SortAttribute attributes, const SortItem &values)
347 {
348   CStdString label;
349   label.Format("%s %s", values.at(FieldVideoCodec).asString(), ByLabel(attributes, values).c_str());
350   return label;
351 }
352
353 string ByVideoAspectRatio(SortAttribute attributes, const SortItem &values)
354 {
355   CStdString label;
356   label.Format("%f %s", values.at(FieldVideoAspectRatio).asString(), ByLabel(attributes, values).c_str());
357   return label;
358 }
359
360 string ByAudioChannels(SortAttribute attributes, const SortItem &values)
361 {
362   CStdString label;
363   label.Format("%i %s", (int)values.at(FieldAudioChannels).asInteger(), ByLabel(attributes, values).c_str());
364   return label;
365 }
366
367 string ByAudioCodec(SortAttribute attributes, const SortItem &values)
368 {
369   CStdString label;
370   label.Format("%s %s", values.at(FieldAudioCodec).asString(), ByLabel(attributes, values).c_str());
371   return label;
372 }
373
374 string ByAudioLanguage(SortAttribute attributes, const SortItem &values)
375 {
376   CStdString label;
377   label.Format("%s %s", values.at(FieldAudioLanguage).asString(), ByLabel(attributes, values).c_str());
378   return label;
379 }
380
381 string BySubtitleLanguage(SortAttribute attributes, const SortItem &values)
382 {
383   CStdString label;
384   label.Format("%s %s", values.at(FieldSubtitleLanguage).asString(), ByLabel(attributes, values).c_str());
385   return label;
386 }
387
388 string ByBitrate(SortAttribute attributes, const SortItem &values)
389 {
390   CStdString label;
391   label.Format("%"PRId64, values.at(FieldBitrate).asInteger());
392   return label;
393 }
394
395 string ByListeners(SortAttribute attributes, const SortItem &values)
396 {
397   CStdString label;
398   label.Format("%i", values.at(FieldListeners).asInteger());
399   return label;
400 }
401
402 string ByRandom(SortAttribute attributes, const SortItem &values)
403 {
404   CStdString label;
405   label.Format("%i", CUtil::GetRandomNumber());
406   return label;
407 }
408
409 string ByChannel(SortAttribute attributes, const SortItem &values)
410 {
411   return values.at(FieldChannelName).asString();
412 }
413
414 string ByDateTaken(SortAttribute attributes, const SortItem &values)
415 {
416   return values.at(FieldDateTaken).asString();
417 }
418
419 bool preliminarySort(const SortItem &left, const SortItem &right, bool handleFolder, bool &result, std::wstring &labelLeft, std::wstring &labelRight)
420 {
421   // make sure both items have the necessary data to do the sorting
422   SortItem::const_iterator itLeftSort, itRightSort;
423   if ((itLeftSort = left.find(FieldSort)) == left.end())
424   {
425     result = false;
426     return true;
427   }
428   if ((itRightSort = right.find(FieldSort)) == right.end())
429   {
430     result = true;
431     return true;
432   }
433
434   // look at special sorting behaviour
435   SortItem::const_iterator itLeft, itRight;
436   SortSpecial leftSortSpecial = SortSpecialNone;
437   SortSpecial rightSortSpecial = SortSpecialNone;
438   if ((itLeft = left.find(FieldSortSpecial)) != left.end() && itLeft->second.asInteger() <= (int64_t)SortSpecialOnBottom)
439     leftSortSpecial = (SortSpecial)itLeft->second.asInteger();
440   if ((itRight = right.find(FieldSortSpecial)) != right.end() && itRight->second.asInteger() <= (int64_t)SortSpecialOnBottom)
441     rightSortSpecial = (SortSpecial)itRight->second.asInteger();
442   
443   // one has a special sort
444   if (leftSortSpecial != rightSortSpecial)
445   {
446     // left should be sorted on top
447     // or right should be sorted on bottom
448     // => left is sorted above right
449     if (leftSortSpecial == SortSpecialOnTop ||
450         rightSortSpecial == SortSpecialOnBottom)
451     {
452       result = true;
453       return true;
454     }
455     
456     // otherwise right is sorted above left
457     result = false;
458     return true;
459   }
460   // both have either sort on top or sort on bottom -> leave as-is
461   else if (leftSortSpecial != SortSpecialNone && leftSortSpecial == rightSortSpecial)
462   {
463     result = false;
464     return true;
465   }
466
467   if (handleFolder)
468   {
469     itLeft = left.find(FieldFolder);
470     itRight = right.find(FieldFolder);
471     if (itLeft != left.end() && itRight != right.end() &&
472         itLeft->second.asBoolean() != itRight->second.asBoolean())
473     {
474       result = itLeft->second.asBoolean();
475       return true;
476     }
477   }
478
479   labelLeft = itLeftSort->second.asWideString();
480   labelRight = itRightSort->second.asWideString();
481
482   return false;
483 }
484
485 bool SorterAscending(const SortItem &left, const SortItem &right)
486 {
487   bool result;
488   std::wstring labelLeft, labelRight;
489   if (preliminarySort(left, right, true, result, labelLeft, labelRight))
490     return result;
491
492   return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
493 }
494
495 bool SorterDescending(const SortItem &left, const SortItem &right)
496 {
497   bool result;
498   std::wstring labelLeft, labelRight;
499   if (preliminarySort(left, right, true, result, labelLeft, labelRight))
500     return result;
501
502   return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
503 }
504
505 bool SorterIgnoreFoldersAscending(const SortItem &left, const SortItem &right)
506 {
507   bool result;
508   std::wstring labelLeft, labelRight;
509   if (preliminarySort(left, right, false, result, labelLeft, labelRight))
510     return result;
511
512   return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
513 }
514
515 bool SorterIgnoreFoldersDescending(const SortItem &left, const SortItem &right)
516 {
517   bool result;
518   std::wstring labelLeft, labelRight;
519   if (preliminarySort(left, right, false, result, labelLeft, labelRight))
520     return result;
521
522   return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
523 }
524
525 bool SorterIndirectAscending(const SortItemPtr &left, const SortItemPtr &right)
526 {
527   return SorterAscending(*left, *right);
528 }
529
530 bool SorterIndirectDescending(const SortItemPtr &left, const SortItemPtr &right)
531 {
532   return SorterDescending(*left, *right);
533 }
534
535 bool SorterIndirectIgnoreFoldersAscending(const SortItemPtr &left, const SortItemPtr &right)
536 {
537   return SorterIgnoreFoldersAscending(*left, *right);
538 }
539
540 bool SorterIndirectIgnoreFoldersDescending(const SortItemPtr &left, const SortItemPtr &right)
541 {
542   return SorterIgnoreFoldersDescending(*left, *right);
543 }
544
545 map<SortBy, SortUtils::SortPreparator> fillPreparators()
546 {
547   map<SortBy, SortUtils::SortPreparator> preparators;
548
549   preparators[SortByNone]                     = NULL;
550   preparators[SortByLabel]                    = ByLabel;
551   preparators[SortByDate]                     = ByDate;
552   preparators[SortBySize]                     = BySize;
553   preparators[SortByFile]                     = ByFile;
554   preparators[SortByPath]                     = ByPath;
555   preparators[SortByDriveType]                = ByDriveType;
556   preparators[SortByTitle]                    = ByTitle;
557   preparators[SortByTrackNumber]              = ByTrackNumber;
558   preparators[SortByTime]                     = ByTime;
559   preparators[SortByArtist]                   = ByArtist;
560   preparators[SortByAlbum]                    = ByAlbum;
561   preparators[SortByAlbumType]                = ByAlbumType;
562   preparators[SortByGenre]                    = ByGenre;
563   preparators[SortByCountry]                  = ByCountry;
564   preparators[SortByYear]                     = ByYear;
565   preparators[SortByRating]                   = ByRating;
566   preparators[SortByVotes]                    = ByVotes;
567   preparators[SortByTop250]                   = ByTop250;
568   preparators[SortByProgramCount]             = ByProgramCount;
569   preparators[SortByPlaylistOrder]            = ByPlaylistOrder;
570   preparators[SortByEpisodeNumber]            = ByEpisodeNumber;
571   preparators[SortBySeason]                   = BySeason;
572   preparators[SortByNumberOfEpisodes]         = ByNumberOfEpisodes;
573   preparators[SortByNumberOfWatchedEpisodes]  = ByNumberOfWatchedEpisodes;
574   preparators[SortByTvShowStatus]             = ByTvShowStatus;
575   preparators[SortByTvShowTitle]              = ByTvShowTitle;
576   preparators[SortBySortTitle]                = BySortTitle;
577   preparators[SortByProductionCode]           = ByProductionCode;
578   preparators[SortByMPAA]                     = ByMPAA;
579   preparators[SortByVideoResolution]          = ByVideoResolution;
580   preparators[SortByVideoCodec]               = ByVideoCodec;
581   preparators[SortByVideoAspectRatio]         = ByVideoAspectRatio;
582   preparators[SortByAudioChannels]            = ByAudioChannels;
583   preparators[SortByAudioCodec]               = ByAudioCodec;
584   preparators[SortByAudioLanguage]            = ByAudioLanguage;
585   preparators[SortBySubtitleLanguage]         = BySubtitleLanguage;
586   preparators[SortByStudio]                   = ByStudio;
587   preparators[SortByDateAdded]                = ByDateAdded;
588   preparators[SortByLastPlayed]               = ByLastPlayed;
589   preparators[SortByPlaycount]                = ByPlaycount;
590   preparators[SortByListeners]                = ByListeners;
591   preparators[SortByBitrate]                  = ByBitrate;
592   preparators[SortByRandom]                   = ByRandom;
593   preparators[SortByChannel]                  = ByChannel;
594   preparators[SortByDateTaken]                = ByDateTaken;
595
596   return preparators;
597 }
598
599 map<SortBy, Fields> fillSortingFields()
600 {
601   map<SortBy, Fields> sortingFields;
602
603   sortingFields.insert(pair<SortBy, Fields>(SortByNone, Fields()));
604
605   sortingFields[SortByLabel].insert(FieldLabel);
606   sortingFields[SortByDate].insert(FieldDate);
607   sortingFields[SortBySize].insert(FieldSize);
608   sortingFields[SortByFile].insert(FieldPath);
609   sortingFields[SortByFile].insert(FieldStartOffset);
610   sortingFields[SortByPath].insert(FieldPath);
611   sortingFields[SortByPath].insert(FieldStartOffset);
612   sortingFields[SortByDriveType].insert(FieldDriveType);
613   sortingFields[SortByTitle].insert(FieldTitle);
614   sortingFields[SortByTrackNumber].insert(FieldTrackNumber);
615   sortingFields[SortByTime].insert(FieldTime);
616   sortingFields[SortByArtist].insert(FieldArtist);
617   sortingFields[SortByArtist].insert(FieldYear);
618   sortingFields[SortByArtist].insert(FieldAlbum);
619   sortingFields[SortByArtist].insert(FieldTrackNumber);
620   sortingFields[SortByAlbum].insert(FieldAlbum);
621   sortingFields[SortByAlbum].insert(FieldArtist);
622   sortingFields[SortByAlbum].insert(FieldTrackNumber);
623   sortingFields[SortByAlbumType].insert(FieldAlbumType);
624   sortingFields[SortByGenre].insert(FieldGenre);
625   sortingFields[SortByCountry].insert(FieldCountry);
626   sortingFields[SortByYear].insert(FieldYear);
627   sortingFields[SortByYear].insert(FieldAirDate);
628   sortingFields[SortByRating].insert(FieldRating);
629   sortingFields[SortByVotes].insert(FieldVotes);
630   sortingFields[SortByTop250].insert(FieldTop250);
631   sortingFields[SortByProgramCount].insert(FieldProgramCount);
632   sortingFields[SortByPlaylistOrder].insert(FieldProgramCount);
633   sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumber);
634   sortingFields[SortByEpisodeNumber].insert(FieldSeason);
635   sortingFields[SortByEpisodeNumber].insert(FieldEpisodeNumberSpecialSort);
636   sortingFields[SortByEpisodeNumber].insert(FieldSeasonSpecialSort);
637   sortingFields[SortByEpisodeNumber].insert(FieldTitle);
638   sortingFields[SortByEpisodeNumber].insert(FieldSortTitle);
639   sortingFields[SortBySeason].insert(FieldSeason);
640   sortingFields[SortBySeason].insert(FieldSeasonSpecialSort);
641   sortingFields[SortByNumberOfEpisodes].insert(FieldNumberOfEpisodes);
642   sortingFields[SortByNumberOfWatchedEpisodes].insert(FieldNumberOfWatchedEpisodes);
643   sortingFields[SortByTvShowStatus].insert(FieldTvShowStatus);
644   sortingFields[SortByTvShowTitle].insert(FieldTvShowTitle);
645   sortingFields[SortBySortTitle].insert(FieldSortTitle);
646   sortingFields[SortBySortTitle].insert(FieldTitle);
647   sortingFields[SortByProductionCode].insert(FieldProductionCode);
648   sortingFields[SortByMPAA].insert(FieldMPAA);
649   sortingFields[SortByVideoResolution].insert(FieldVideoResolution);
650   sortingFields[SortByVideoCodec].insert(FieldVideoCodec);
651   sortingFields[SortByVideoAspectRatio].insert(FieldVideoAspectRatio);
652   sortingFields[SortByAudioChannels].insert(FieldAudioChannels);
653   sortingFields[SortByAudioCodec].insert(FieldAudioCodec);
654   sortingFields[SortByAudioLanguage].insert(FieldAudioLanguage);
655   sortingFields[SortBySubtitleLanguage].insert(FieldSubtitleLanguage);
656   sortingFields[SortByStudio].insert(FieldStudio);
657   sortingFields[SortByDateAdded].insert(FieldDateAdded);
658   sortingFields[SortByDateAdded].insert(FieldId);
659   sortingFields[SortByLastPlayed].insert(FieldLastPlayed);
660   sortingFields[SortByPlaycount].insert(FieldPlaycount);
661   sortingFields[SortByListeners].insert(FieldListeners);
662   sortingFields[SortByBitrate].insert(FieldBitrate);
663   sortingFields[SortByChannel].insert(FieldChannelName);
664   sortingFields[SortByDateTaken].insert(FieldDateTaken);
665   sortingFields.insert(pair<SortBy, Fields>(SortByRandom, Fields()));
666
667   return sortingFields;
668 }
669
670 map<SortBy, SortUtils::SortPreparator> SortUtils::m_preparators = fillPreparators();
671 map<SortBy, Fields> SortUtils::m_sortingFields = fillSortingFields();
672
673 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, DatabaseResults& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
674 {
675   if (sortBy != SortByNone)
676   {
677     // get the matching SortPreparator
678     SortPreparator preparator = getPreparator(sortBy);
679     if (preparator != NULL)
680     {
681       Fields sortingFields = GetFieldsForSorting(sortBy);
682
683       // Prepare the string used for sorting and store it under FieldSort
684       for (DatabaseResults::iterator item = items.begin(); item != items.end(); item++)
685       {
686         // add all fields to the item that are required for sorting if they are currently missing
687         for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); field++)
688         {
689           if (item->find(*field) == item->end())
690             item->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
691         }
692
693         CStdStringW sortLabel;
694         g_charsetConverter.utf8ToW(preparator(attributes, *item), sortLabel, false);
695         item->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
696       }
697
698       // Do the sorting
699       std::stable_sort(items.begin(), items.end(), getSorter(sortOrder, attributes));
700     }
701   }
702
703   if (limitStart > 0 && (size_t)limitStart < items.size())
704   {
705     items.erase(items.begin(), items.begin() + limitStart);
706     limitEnd -= limitStart;
707   }
708   if (limitEnd > 0 && (size_t)limitEnd < items.size())
709     items.erase(items.begin() + limitEnd, items.end());
710 }
711
712 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, SortItems& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
713 {
714   if (sortBy != SortByNone)
715   {
716     // get the matching SortPreparator
717     SortPreparator preparator = getPreparator(sortBy);
718     if (preparator != NULL)
719     {
720       Fields sortingFields = GetFieldsForSorting(sortBy);
721
722       // Prepare the string used for sorting and store it under FieldSort
723       for (SortItems::iterator item = items.begin(); item != items.end(); item++)
724       {
725         // add all fields to the item that are required for sorting if they are currently missing
726         for (Fields::const_iterator field = sortingFields.begin(); field != sortingFields.end(); field++)
727         {
728           if ((*item)->find(*field) == (*item)->end())
729             (*item)->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
730         }
731
732         CStdStringW sortLabel;
733         g_charsetConverter.utf8ToW(preparator(attributes, **item), sortLabel, false);
734         (*item)->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
735       }
736
737       // Do the sorting
738       std::stable_sort(items.begin(), items.end(), getSorterIndirect(sortOrder, attributes));
739     }
740   }
741
742   if (limitStart > 0 && (size_t)limitStart < items.size())
743   {
744     items.erase(items.begin(), items.begin() + limitStart);
745     limitEnd -= limitStart;
746   }
747   if (limitEnd > 0 && (size_t)limitEnd < items.size())
748     items.erase(items.begin() + limitEnd, items.end());
749 }
750
751 void SortUtils::Sort(const SortDescription &sortDescription, DatabaseResults& items)
752 {
753   Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
754 }
755
756 void SortUtils::Sort(const SortDescription &sortDescription, SortItems& items)
757 {
758   Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
759 }
760
761 bool SortUtils::SortFromDataset(const SortDescription &sortDescription, MediaType mediaType, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
762 {
763   FieldList fields;
764   if (!DatabaseUtils::GetSelectFields(SortUtils::GetFieldsForSorting(sortDescription.sortBy), mediaType, fields))
765     fields.clear();
766
767   if (!DatabaseUtils::GetDatabaseResults(mediaType, fields, dataset, results))
768     return false;
769
770   SortDescription sorting = sortDescription;
771   if (sortDescription.sortBy == SortByNone)
772   {
773     sorting.limitStart = 0;
774     sorting.limitEnd = -1;
775   }
776
777   Sort(sorting, results);
778
779   return true;
780 }
781
782 const SortUtils::SortPreparator& SortUtils::getPreparator(SortBy sortBy)
783 {
784   map<SortBy, SortPreparator>::const_iterator it = m_preparators.find(sortBy);
785   if (it != m_preparators.end())
786     return it->second;
787
788   return m_preparators[SortByNone];
789 }
790
791 SortUtils::Sorter SortUtils::getSorter(SortOrder sortOrder, SortAttribute attributes)
792 {
793   if (attributes & SortAttributeIgnoreFolders)
794     return sortOrder == SortOrderDescending ? SorterIgnoreFoldersDescending : SorterIgnoreFoldersAscending;
795
796   return sortOrder == SortOrderDescending ? SorterDescending : SorterAscending;
797 }
798
799 SortUtils::SorterIndirect SortUtils::getSorterIndirect(SortOrder sortOrder, SortAttribute attributes)
800 {
801   if (attributes & SortAttributeIgnoreFolders)
802     return sortOrder == SortOrderDescending ? SorterIndirectIgnoreFoldersDescending : SorterIndirectIgnoreFoldersAscending;
803
804   return sortOrder == SortOrderDescending ? SorterIndirectDescending : SorterIndirectAscending;
805 }
806
807 const Fields& SortUtils::GetFieldsForSorting(SortBy sortBy)
808 {
809   map<SortBy, Fields>::const_iterator it = m_sortingFields.find(sortBy);
810   if (it != m_sortingFields.end())
811     return it->second;
812
813   return m_sortingFields[SortByNone];
814 }
815
816 string SortUtils::RemoveArticles(const string &label)
817 {
818   for (unsigned int i = 0; i < g_advancedSettings.m_vecTokens.size(); ++i)
819   {
820     if (g_advancedSettings.m_vecTokens[i].size() < label.size() &&
821         strnicmp(g_advancedSettings.m_vecTokens[i].c_str(), label.c_str(), g_advancedSettings.m_vecTokens[i].size()) == 0)
822       return label.substr(g_advancedSettings.m_vecTokens[i].size());
823   }
824
825   return label;
826 }
827
828 typedef struct
829 {
830   SortBy        sort;
831   SORT_METHOD   old;
832   SortAttribute flags;
833   int           label;
834 } sort_map;
835
836 const sort_map table[] = {
837   { SortByLabel,                    SORT_METHOD_LABEL,                        SortAttributeNone,          551 },
838   { SortByLabel,                    SORT_METHOD_LABEL_IGNORE_THE,             SortAttributeIgnoreArticle, 551 },
839   { SortByLabel,                    SORT_METHOD_LABEL_IGNORE_FOLDERS,         SortAttributeIgnoreFolders, 551 },
840   { SortByDate,                     SORT_METHOD_DATE,                         SortAttributeNone,          552 },
841   { SortBySize,                     SORT_METHOD_SIZE,                         SortAttributeNone,          553 },
842   { SortByBitrate,                  SORT_METHOD_BITRATE,                      SortAttributeNone,          623 },
843   { SortByDriveType,                SORT_METHOD_DRIVE_TYPE,                   SortAttributeNone,          564 },
844   { SortByTrackNumber,              SORT_METHOD_TRACKNUM,                     SortAttributeNone,          554 },
845   { SortByEpisodeNumber,            SORT_METHOD_EPISODE,                      SortAttributeNone,          20359 },// 20360 "Episodes" used for SORT_METHOD_EPISODE for sorting tvshows by episode count
846   { SortByTime,                     SORT_METHOD_DURATION,                     SortAttributeNone,          180 },
847   { SortByTime,                     SORT_METHOD_VIDEO_RUNTIME,                SortAttributeNone,          180 },
848   { SortByTitle,                    SORT_METHOD_TITLE,                        SortAttributeNone,          556 },
849   { SortByTitle,                    SORT_METHOD_TITLE_IGNORE_THE,             SortAttributeIgnoreArticle, 556 },
850   { SortByTitle,                    SORT_METHOD_VIDEO_TITLE,                  SortAttributeNone,          556 },
851   { SortByArtist,                   SORT_METHOD_ARTIST,                       SortAttributeNone,          557 },
852   { SortByArtist,                   SORT_METHOD_ARTIST_IGNORE_THE,            SortAttributeIgnoreArticle, 557 },
853   { SortByAlbum,                    SORT_METHOD_ALBUM,                        SortAttributeNone,          558 },
854   { SortByAlbum,                    SORT_METHOD_ALBUM_IGNORE_THE,             SortAttributeIgnoreArticle, 558 },
855   { SortByGenre,                    SORT_METHOD_GENRE,                        SortAttributeNone,          515 },
856   { SortByCountry,                  SORT_METHOD_COUNTRY,                      SortAttributeNone,          574 },
857   { SortByDateAdded,                SORT_METHOD_DATEADDED,                    SortAttributeIgnoreFolders, 570 },
858   { SortByFile,                     SORT_METHOD_FILE,                         SortAttributeIgnoreFolders, 561 },
859   { SortByRating,                   SORT_METHOD_SONG_RATING,                  SortAttributeNone,          563 },
860   { SortByRating,                   SORT_METHOD_VIDEO_RATING,                 SortAttributeIgnoreFolders, 563 },
861   { SortBySortTitle,                SORT_METHOD_VIDEO_SORT_TITLE,             SortAttributeIgnoreFolders, 171 },
862   { SortBySortTitle,                SORT_METHOD_VIDEO_SORT_TITLE_IGNORE_THE,  (SortAttribute)(SortAttributeIgnoreFolders | SortAttributeIgnoreArticle), 171 },
863   { SortByYear,                     SORT_METHOD_YEAR,                         SortAttributeIgnoreFolders, 562 },
864   { SortByProductionCode,           SORT_METHOD_PRODUCTIONCODE,               SortAttributeNone,          20368 },
865   { SortByProgramCount,             SORT_METHOD_PROGRAM_COUNT,                SortAttributeNone,          567 }, // label is "play count"
866   { SortByPlaylistOrder,            SORT_METHOD_PLAYLIST_ORDER,               SortAttributeIgnoreFolders, 559 },
867   { SortByMPAA,                     SORT_METHOD_MPAA_RATING,                  SortAttributeNone,          20074 },
868   { SortByStudio,                   SORT_METHOD_STUDIO,                       SortAttributeNone,          572 },
869   { SortByStudio,                   SORT_METHOD_STUDIO_IGNORE_THE,            SortAttributeIgnoreArticle, 572 },
870   { SortByPath,                     SORT_METHOD_FULLPATH,                     SortAttributeNone,          573 },
871   { SortByLastPlayed,               SORT_METHOD_LASTPLAYED,                   SortAttributeIgnoreFolders, 568 },
872   { SortByPlaycount,                SORT_METHOD_PLAYCOUNT,                    SortAttributeIgnoreFolders, 567 },
873   { SortByListeners,                SORT_METHOD_LISTENERS,                    SortAttributeNone,          20455 },
874   { SortByChannel,                  SORT_METHOD_CHANNEL,                      SortAttributeNone,          19029 },
875   { SortByDateTaken,                SORT_METHOD_DATE_TAKEN,                   SortAttributeIgnoreFolders, 577 },
876   { SortByNone,                     SORT_METHOD_NONE,                         SortAttributeNone,          16018 },
877   // the following have no corresponding SORT_METHOD_*
878   { SortByAlbumType,                SORT_METHOD_NONE,                         SortAttributeNone,          564 },
879   { SortByVotes,                    SORT_METHOD_NONE,                         SortAttributeNone,          205 },
880   { SortByTop250,                   SORT_METHOD_NONE,                         SortAttributeNone,          13409 },
881   { SortByMPAA,                     SORT_METHOD_NONE,                         SortAttributeNone,          20074 },
882   { SortByDateAdded,                SORT_METHOD_NONE,                         SortAttributeNone,          570 },
883   { SortByTvShowTitle,              SORT_METHOD_NONE,                         SortAttributeNone,          20364 },
884   { SortByTvShowStatus,             SORT_METHOD_NONE,                         SortAttributeNone,          126 },
885   { SortBySeason,                   SORT_METHOD_NONE,                         SortAttributeNone,          20373 },
886   { SortByNumberOfEpisodes,         SORT_METHOD_NONE,                         SortAttributeNone,          20360 },
887   { SortByNumberOfWatchedEpisodes,  SORT_METHOD_NONE,                         SortAttributeNone,          21441 },
888   { SortByVideoResolution,          SORT_METHOD_NONE,                         SortAttributeNone,          21443 },
889   { SortByVideoCodec,               SORT_METHOD_NONE,                         SortAttributeNone,          21445 },
890   { SortByVideoAspectRatio,         SORT_METHOD_NONE,                         SortAttributeNone,          21374 },
891   { SortByAudioChannels,            SORT_METHOD_NONE,                         SortAttributeNone,          21444 },
892   { SortByAudioCodec,               SORT_METHOD_NONE,                         SortAttributeNone,          21446 },
893   { SortByAudioLanguage,            SORT_METHOD_NONE,                         SortAttributeNone,          21447 },
894   { SortBySubtitleLanguage,         SORT_METHOD_NONE,                         SortAttributeNone,          21448 },
895   { SortByRandom,                   SORT_METHOD_NONE,                         SortAttributeNone,          590 }
896 };
897
898 SORT_METHOD SortUtils::TranslateOldSortMethod(SortBy sortBy, bool ignoreArticle)
899 {
900   for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
901   {
902     if (table[i].sort == sortBy)
903     {
904       if (ignoreArticle == ((table[i].flags & SortAttributeIgnoreArticle) == SortAttributeIgnoreArticle))
905         return table[i].old;
906     }
907   }
908   for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
909   {
910     if (table[i].sort == sortBy)
911       return table[i].old;
912   }
913   return SORT_METHOD_NONE;
914 }
915
916 SortDescription SortUtils::TranslateOldSortMethod(SORT_METHOD sortBy)
917 {
918   SortDescription description;
919   for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
920   {
921     if (table[i].old == sortBy)
922     {
923       description.sortBy = table[i].sort;
924       description.sortAttributes = table[i].flags;
925       break;
926     }
927   }
928   return description;
929 }
930
931 int SortUtils::GetSortLabel(SortBy sortBy)
932 {
933   for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
934   {
935     if (table[i].sort == sortBy)
936       return table[i].label;
937   }
938   return 16018; // None
939 }