2 * Copyright (C) 2012-2013 Team XBMC
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)
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.
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/>.
21 #include "SortUtils.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"
33 string ArrayToString(SortAttribute attributes, const CVariant &variant, const string &seperator = " / ")
35 vector<string> strArray;
36 if (variant.isArray())
38 for (CVariant::const_iterator_array it = variant.begin_array(); it != variant.end_array(); it++)
40 if (attributes & SortAttributeIgnoreArticle)
41 strArray.push_back(SortUtils::RemoveArticles(it->asString()));
43 strArray.push_back(it->asString());
46 return StringUtils::Join(strArray, seperator);
48 else if (variant.isString())
50 if (attributes & SortAttributeIgnoreArticle)
51 return SortUtils::RemoveArticles(variant.asString());
53 return variant.asString();
59 string ByLabel(SortAttribute attributes, const SortItem &values)
61 if (attributes & SortAttributeIgnoreArticle)
62 return SortUtils::RemoveArticles(values.at(FieldLabel).asString());
64 return values.at(FieldLabel).asString();
67 string ByFile(SortAttribute attributes, const SortItem &values)
69 CURL url(values.at(FieldPath).asString());
72 label.Format("%s %d", url.GetFileNameWithoutPath().c_str(), values.at(FieldStartOffset).asInteger());
76 string ByPath(SortAttribute attributes, const SortItem &values)
79 label.Format("%s %d", values.at(FieldPath).asString().c_str(), values.at(FieldStartOffset).asInteger());
83 string ByLastPlayed(SortAttribute attributes, const SortItem &values)
86 label.Format("%s %s", values.at(FieldLastPlayed).asString().c_str(), ByLabel(attributes, values).c_str());
90 string ByPlaycount(SortAttribute attributes, const SortItem &values)
93 label.Format("%i %s", (int)values.at(FieldPlaycount).asInteger(), ByLabel(attributes, values).c_str());
97 string ByDate(SortAttribute attributes, const SortItem &values)
99 return values.at(FieldDate).asString() + " " + ByLabel(attributes, values);
102 string ByDateAdded(SortAttribute attributes, const SortItem &values)
105 label.Format("%s %d", values.at(FieldDateAdded).asString().c_str(), (int)values.at(FieldId).asInteger());
109 string BySize(SortAttribute attributes, const SortItem &values)
112 label.Format("%"PRId64, values.at(FieldSize).asInteger());
116 string ByDriveType(SortAttribute attributes, const SortItem &values)
119 label.Format("%d %s", (int)values.at(FieldDriveType).asInteger(), ByLabel(attributes, values).c_str());
123 string ByTitle(SortAttribute attributes, const SortItem &values)
125 if (attributes & SortAttributeIgnoreArticle)
126 return SortUtils::RemoveArticles(values.at(FieldTitle).asString());
128 return values.at(FieldTitle).asString();
131 string ByAlbum(SortAttribute attributes, const SortItem &values)
133 string album = values.at(FieldAlbum).asString();
134 if (attributes & SortAttributeIgnoreArticle)
135 album = SortUtils::RemoveArticles(album);
138 label.Format("%s %s", album, ArrayToString(attributes, values.at(FieldArtist)));
140 const CVariant &track = values.at(FieldTrackNumber);
142 label.AppendFormat(" %i", (int)track.asInteger());
147 string ByAlbumType(SortAttribute attributes, const SortItem &values)
149 return values.at(FieldAlbumType).asString() + " " + ByLabel(attributes, values);
152 string ByArtist(SortAttribute attributes, const SortItem &values)
154 CStdString label = ArrayToString(attributes, values.at(FieldArtist));
156 const CVariant &year = values.at(FieldYear);
157 if (g_advancedSettings.m_bMusicLibraryAlbumsSortByArtistThenYear &&
159 label.AppendFormat(" %i", (int)year.asInteger());
161 const CVariant &album = values.at(FieldAlbum);
163 label += " " + SortUtils::RemoveArticles(album.asString());
165 const CVariant &track = values.at(FieldTrackNumber);
167 label.AppendFormat(" %i", (int)track.asInteger());
172 string ByTrackNumber(SortAttribute attributes, const SortItem &values)
175 label.Format("%i", (int)values.at(FieldTrackNumber).asInteger());
179 string ByTime(SortAttribute attributes, const SortItem &values)
182 const CVariant &time = values.at(FieldTime);
183 if (time.isInteger())
184 label.Format("%i", (int)time.asInteger());
186 label.Format("%s", time.asString());
190 string ByProgramCount(SortAttribute attributes, const SortItem &values)
193 label.Format("%i", (int)values.at(FieldProgramCount).asInteger());
197 string ByPlaylistOrder(SortAttribute attributes, const SortItem &values)
199 // TODO: Playlist order is hacked into program count variable (not nice, but ok until 2.0)
200 return ByProgramCount(attributes, values);
203 string ByGenre(SortAttribute attributes, const SortItem &values)
205 return ArrayToString(attributes, values.at(FieldGenre));
208 string ByCountry(SortAttribute attributes, const SortItem &values)
210 return ArrayToString(attributes, values.at(FieldCountry));
213 string ByYear(SortAttribute attributes, const SortItem &values)
216 const CVariant &airDate = values.at(FieldAirDate);
217 if (!airDate.isNull())
218 label = airDate.asString() + " ";
220 label.AppendFormat("%i %s", (int)values.at(FieldYear).asInteger(), ByLabel(attributes, values).c_str());
225 string BySortTitle(SortAttribute attributes, const SortItem &values)
227 string title = values.at(FieldSortTitle).asString();
229 title = values.at(FieldTitle).asString();
231 if (attributes & SortAttributeIgnoreArticle)
232 title = SortUtils::RemoveArticles(title);
237 string ByRating(SortAttribute attributes, const SortItem &values)
240 label.Format("%f %s", values.at(FieldRating).asFloat(), ByLabel(attributes, values).c_str());
244 string ByVotes(SortAttribute attributes, const SortItem &values)
247 label.Format("%d %s", (int)values.at(FieldVotes).asInteger(), ByLabel(attributes, values).c_str());
251 string ByTop250(SortAttribute attributes, const SortItem &values)
254 label.Format("%d %s", (int)values.at(FieldTop250).asInteger(), ByLabel(attributes, values).c_str());
258 string ByMPAA(SortAttribute attributes, const SortItem &values)
260 return values.at(FieldMPAA).asString() + " " + ByLabel(attributes, values);
263 string ByStudio(SortAttribute attributes, const SortItem &values)
265 return ArrayToString(attributes, values.at(FieldStudio));
268 string ByEpisodeNumber(SortAttribute attributes, const SortItem &values)
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)
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());
285 num = ((uint64_t)values.at(FieldSeason).asInteger() << 32) + (values.at(FieldEpisodeNumber).asInteger() << 16);
288 if (values.find(FieldMediaType) != values.end() && values.at(FieldMediaType).asInteger() == MediaTypeMovie)
289 title = BySortTitle(attributes, values);
291 title = ByLabel(attributes, values);
294 label.Format("%"PRIu64" %s", num, title.c_str());
298 string BySeason(SortAttribute attributes, const SortItem &values)
300 int season = (int)values.at(FieldSeason).asInteger();
301 const CVariant &specialSeason = values.at(FieldSeasonSpecialSort);
302 if (!specialSeason.isNull())
303 season = (int)specialSeason.asInteger();
306 label.Format("%i %s", season, ByLabel(attributes, values).c_str());
310 string ByNumberOfEpisodes(SortAttribute attributes, const SortItem &values)
313 label.Format("%i %s", (int)values.at(FieldNumberOfEpisodes).asInteger(), ByLabel(attributes, values).c_str());
317 string ByNumberOfWatchedEpisodes(SortAttribute attributes, const SortItem &values)
320 label.Format("%i %s", (int)values.at(FieldNumberOfWatchedEpisodes).asInteger(), ByLabel(attributes, values).c_str());
324 string ByTvShowStatus(SortAttribute attributes, const SortItem &values)
326 return values.at(FieldTvShowStatus).asString() + " " + ByLabel(attributes, values);
329 string ByTvShowTitle(SortAttribute attributes, const SortItem &values)
331 return values.at(FieldTvShowTitle).asString() + " " + ByLabel(attributes, values);
334 string ByProductionCode(SortAttribute attributes, const SortItem &values)
336 return values.at(FieldProductionCode).asString();
339 string ByVideoResolution(SortAttribute attributes, const SortItem &values)
342 label.Format("%i %s", (int)values.at(FieldVideoResolution).asInteger(), ByLabel(attributes, values).c_str());
346 string ByVideoCodec(SortAttribute attributes, const SortItem &values)
349 label.Format("%s %s", values.at(FieldVideoCodec).asString(), ByLabel(attributes, values).c_str());
353 string ByVideoAspectRatio(SortAttribute attributes, const SortItem &values)
356 label.Format("%f %s", values.at(FieldVideoAspectRatio).asString(), ByLabel(attributes, values).c_str());
360 string ByAudioChannels(SortAttribute attributes, const SortItem &values)
363 label.Format("%i %s", (int)values.at(FieldAudioChannels).asInteger(), ByLabel(attributes, values).c_str());
367 string ByAudioCodec(SortAttribute attributes, const SortItem &values)
370 label.Format("%s %s", values.at(FieldAudioCodec).asString(), ByLabel(attributes, values).c_str());
374 string ByAudioLanguage(SortAttribute attributes, const SortItem &values)
377 label.Format("%s %s", values.at(FieldAudioLanguage).asString(), ByLabel(attributes, values).c_str());
381 string BySubtitleLanguage(SortAttribute attributes, const SortItem &values)
384 label.Format("%s %s", values.at(FieldSubtitleLanguage).asString(), ByLabel(attributes, values).c_str());
388 string ByBitrate(SortAttribute attributes, const SortItem &values)
391 label.Format("%"PRId64, values.at(FieldBitrate).asInteger());
395 string ByListeners(SortAttribute attributes, const SortItem &values)
398 label.Format("%i", values.at(FieldListeners).asInteger());
402 string ByRandom(SortAttribute attributes, const SortItem &values)
405 label.Format("%i", CUtil::GetRandomNumber());
409 string ByChannel(SortAttribute attributes, const SortItem &values)
411 return values.at(FieldChannelName).asString();
414 string ByDateTaken(SortAttribute attributes, const SortItem &values)
416 return values.at(FieldDateTaken).asString();
419 bool preliminarySort(const SortItem &left, const SortItem &right, bool handleFolder, bool &result, std::wstring &labelLeft, std::wstring &labelRight)
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())
428 if ((itRightSort = right.find(FieldSort)) == right.end())
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();
443 // one has a special sort
444 if (leftSortSpecial != rightSortSpecial)
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)
456 // otherwise right is sorted above left
460 // both have either sort on top or sort on bottom -> leave as-is
461 else if (leftSortSpecial != SortSpecialNone && leftSortSpecial == rightSortSpecial)
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())
474 result = itLeft->second.asBoolean();
479 labelLeft = itLeftSort->second.asWideString();
480 labelRight = itRightSort->second.asWideString();
485 bool SorterAscending(const SortItem &left, const SortItem &right)
488 std::wstring labelLeft, labelRight;
489 if (preliminarySort(left, right, true, result, labelLeft, labelRight))
492 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
495 bool SorterDescending(const SortItem &left, const SortItem &right)
498 std::wstring labelLeft, labelRight;
499 if (preliminarySort(left, right, true, result, labelLeft, labelRight))
502 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
505 bool SorterIgnoreFoldersAscending(const SortItem &left, const SortItem &right)
508 std::wstring labelLeft, labelRight;
509 if (preliminarySort(left, right, false, result, labelLeft, labelRight))
512 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) < 0;
515 bool SorterIgnoreFoldersDescending(const SortItem &left, const SortItem &right)
518 std::wstring labelLeft, labelRight;
519 if (preliminarySort(left, right, false, result, labelLeft, labelRight))
522 return StringUtils::AlphaNumericCompare(labelLeft.c_str(), labelRight.c_str()) > 0;
525 bool SorterIndirectAscending(const SortItemPtr &left, const SortItemPtr &right)
527 return SorterAscending(*left, *right);
530 bool SorterIndirectDescending(const SortItemPtr &left, const SortItemPtr &right)
532 return SorterDescending(*left, *right);
535 bool SorterIndirectIgnoreFoldersAscending(const SortItemPtr &left, const SortItemPtr &right)
537 return SorterIgnoreFoldersAscending(*left, *right);
540 bool SorterIndirectIgnoreFoldersDescending(const SortItemPtr &left, const SortItemPtr &right)
542 return SorterIgnoreFoldersDescending(*left, *right);
545 map<SortBy, SortUtils::SortPreparator> fillPreparators()
547 map<SortBy, SortUtils::SortPreparator> preparators;
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;
599 map<SortBy, Fields> fillSortingFields()
601 map<SortBy, Fields> sortingFields;
603 sortingFields.insert(pair<SortBy, Fields>(SortByNone, Fields()));
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()));
667 return sortingFields;
670 map<SortBy, SortUtils::SortPreparator> SortUtils::m_preparators = fillPreparators();
671 map<SortBy, Fields> SortUtils::m_sortingFields = fillSortingFields();
673 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, DatabaseResults& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
675 if (sortBy != SortByNone)
677 // get the matching SortPreparator
678 SortPreparator preparator = getPreparator(sortBy);
679 if (preparator != NULL)
681 Fields sortingFields = GetFieldsForSorting(sortBy);
683 // Prepare the string used for sorting and store it under FieldSort
684 for (DatabaseResults::iterator item = items.begin(); item != items.end(); item++)
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++)
689 if (item->find(*field) == item->end())
690 item->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
693 CStdStringW sortLabel;
694 g_charsetConverter.utf8ToW(preparator(attributes, *item), sortLabel, false);
695 item->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
699 std::stable_sort(items.begin(), items.end(), getSorter(sortOrder, attributes));
703 if (limitStart > 0 && (size_t)limitStart < items.size())
705 items.erase(items.begin(), items.begin() + limitStart);
706 limitEnd -= limitStart;
708 if (limitEnd > 0 && (size_t)limitEnd < items.size())
709 items.erase(items.begin() + limitEnd, items.end());
712 void SortUtils::Sort(SortBy sortBy, SortOrder sortOrder, SortAttribute attributes, SortItems& items, int limitEnd /* = -1 */, int limitStart /* = 0 */)
714 if (sortBy != SortByNone)
716 // get the matching SortPreparator
717 SortPreparator preparator = getPreparator(sortBy);
718 if (preparator != NULL)
720 Fields sortingFields = GetFieldsForSorting(sortBy);
722 // Prepare the string used for sorting and store it under FieldSort
723 for (SortItems::iterator item = items.begin(); item != items.end(); item++)
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++)
728 if ((*item)->find(*field) == (*item)->end())
729 (*item)->insert(pair<Field, CVariant>(*field, CVariant::ConstNullVariant));
732 CStdStringW sortLabel;
733 g_charsetConverter.utf8ToW(preparator(attributes, **item), sortLabel, false);
734 (*item)->insert(pair<Field, CVariant>(FieldSort, CVariant(sortLabel)));
738 std::stable_sort(items.begin(), items.end(), getSorterIndirect(sortOrder, attributes));
742 if (limitStart > 0 && (size_t)limitStart < items.size())
744 items.erase(items.begin(), items.begin() + limitStart);
745 limitEnd -= limitStart;
747 if (limitEnd > 0 && (size_t)limitEnd < items.size())
748 items.erase(items.begin() + limitEnd, items.end());
751 void SortUtils::Sort(const SortDescription &sortDescription, DatabaseResults& items)
753 Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
756 void SortUtils::Sort(const SortDescription &sortDescription, SortItems& items)
758 Sort(sortDescription.sortBy, sortDescription.sortOrder, sortDescription.sortAttributes, items, sortDescription.limitEnd, sortDescription.limitStart);
761 bool SortUtils::SortFromDataset(const SortDescription &sortDescription, MediaType mediaType, const std::auto_ptr<dbiplus::Dataset> &dataset, DatabaseResults &results)
764 if (!DatabaseUtils::GetSelectFields(SortUtils::GetFieldsForSorting(sortDescription.sortBy), mediaType, fields))
767 if (!DatabaseUtils::GetDatabaseResults(mediaType, fields, dataset, results))
770 SortDescription sorting = sortDescription;
771 if (sortDescription.sortBy == SortByNone)
773 sorting.limitStart = 0;
774 sorting.limitEnd = -1;
777 Sort(sorting, results);
782 const SortUtils::SortPreparator& SortUtils::getPreparator(SortBy sortBy)
784 map<SortBy, SortPreparator>::const_iterator it = m_preparators.find(sortBy);
785 if (it != m_preparators.end())
788 return m_preparators[SortByNone];
791 SortUtils::Sorter SortUtils::getSorter(SortOrder sortOrder, SortAttribute attributes)
793 if (attributes & SortAttributeIgnoreFolders)
794 return sortOrder == SortOrderDescending ? SorterIgnoreFoldersDescending : SorterIgnoreFoldersAscending;
796 return sortOrder == SortOrderDescending ? SorterDescending : SorterAscending;
799 SortUtils::SorterIndirect SortUtils::getSorterIndirect(SortOrder sortOrder, SortAttribute attributes)
801 if (attributes & SortAttributeIgnoreFolders)
802 return sortOrder == SortOrderDescending ? SorterIndirectIgnoreFoldersDescending : SorterIndirectIgnoreFoldersAscending;
804 return sortOrder == SortOrderDescending ? SorterIndirectDescending : SorterIndirectAscending;
807 const Fields& SortUtils::GetFieldsForSorting(SortBy sortBy)
809 map<SortBy, Fields>::const_iterator it = m_sortingFields.find(sortBy);
810 if (it != m_sortingFields.end())
813 return m_sortingFields[SortByNone];
816 string SortUtils::RemoveArticles(const string &label)
818 for (unsigned int i = 0; i < g_advancedSettings.m_vecTokens.size(); ++i)
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());
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 }
898 SORT_METHOD SortUtils::TranslateOldSortMethod(SortBy sortBy, bool ignoreArticle)
900 for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
902 if (table[i].sort == sortBy)
904 if (ignoreArticle == ((table[i].flags & SortAttributeIgnoreArticle) == SortAttributeIgnoreArticle))
908 for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
910 if (table[i].sort == sortBy)
913 return SORT_METHOD_NONE;
916 SortDescription SortUtils::TranslateOldSortMethod(SORT_METHOD sortBy)
918 SortDescription description;
919 for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
921 if (table[i].old == sortBy)
923 description.sortBy = table[i].sort;
924 description.sortAttributes = table[i].flags;
931 int SortUtils::GetSortLabel(SortBy sortBy)
933 for (size_t i = 0; i < sizeof(table) / sizeof(sort_map); i++)
935 if (table[i].sort == sortBy)
936 return table[i].label;
938 return 16018; // None