/*
* Copyright (C) 2005-2013 Team XBMC
* http://xbmc.org
*
* This Program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This Program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* 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 XBMC; see the file COPYING. If not, see
* .
*
*/
#include "system.h"
#include "settings/AdvancedSettings.h"
#include "SortFileItem.h"
#include "video/VideoInfoTag.h"
#include "music/tags/MusicInfoTag.h"
#include "FileItem.h"
#include "URL.h"
#include "utils/log.h"
#ifndef PRId64
#ifdef _MSC_VER
#define PRId64 "I64d"
#else
#define PRId64 "lld"
#endif
#endif
#define RETURN_IF_NULL(x,y) if ((x) == NULL) { CLog::Log(LOGWARNING, "%s, sort item is null", __FUNCTION__); return y; }
CStdString SSortFileItem::RemoveArticles(const CStdString &label)
{
for (unsigned int i=0;iSortsOnTop() != right->SortsOnTop())
return left->SortsOnTop();
if (left->SortsOnBottom() != right->SortsOnBottom())
return !left->SortsOnBottom();
if (left->SortsOnTop() || left->SortsOnBottom())
return false; // both have either sort on top or sort on bottom -> leave as-is
if (left->m_bIsFolder == right->m_bIsFolder)
return StringUtils::AlphaNumericCompare(left->GetSortLabel().c_str(),right->GetSortLabel().c_str()) < 0;
return left->m_bIsFolder;
}
bool SSortFileItem::Descending(const CFileItemPtr &left, const CFileItemPtr &right)
{
// sanity
RETURN_IF_NULL(left,false); RETURN_IF_NULL(right,false);
// ignore items that should sort on top or bottom
if (left->SortsOnTop() != right->SortsOnTop())
return left->SortsOnTop();
if (left->SortsOnBottom() != right->SortsOnBottom())
return !left->SortsOnBottom();
if (left->SortsOnTop() || left->SortsOnBottom())
return false; // both have either sort on top or sort on bottom -> leave as-is
if (left->m_bIsFolder == right->m_bIsFolder)
return StringUtils::AlphaNumericCompare(left->GetSortLabel().c_str(),right->GetSortLabel().c_str()) > 0;
return left->m_bIsFolder;
}
bool SSortFileItem::IgnoreFoldersAscending(const CFileItemPtr &left, const CFileItemPtr &right)
{
// sanity
RETURN_IF_NULL(left,false); RETURN_IF_NULL(right,false);
// ignore items that should sort on top or bottom
if (left->SortsOnTop() != right->SortsOnTop())
return left->SortsOnTop();
if (left->SortsOnBottom() != right->SortsOnBottom())
return !left->SortsOnBottom();
if (left->SortsOnTop() || left->SortsOnBottom())
return false; // both have either sort on top or sort on bottom -> leave as-is
return StringUtils::AlphaNumericCompare(left->GetSortLabel().c_str(),right->GetSortLabel().c_str()) < 0;
}
bool SSortFileItem::IgnoreFoldersDescending(const CFileItemPtr &left, const CFileItemPtr &right)
{
// sanity
RETURN_IF_NULL(left,false); RETURN_IF_NULL(right,false);
// ignore items that should sort on top or bottom
if (left->SortsOnTop() != right->SortsOnTop())
return left->SortsOnTop();
if (left->SortsOnBottom() != right->SortsOnBottom())
return !left->SortsOnBottom();
if (left->SortsOnTop() || left->SortsOnBottom())
return false; // both have either sort on top or sort on bottom -> leave as-is
return StringUtils::AlphaNumericCompare(left->GetSortLabel().c_str(),right->GetSortLabel().c_str()) > 0;
}
void SSortFileItem::ByLabel(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetLabel());
}
void SSortFileItem::ByLabelNoThe(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(RemoveArticles(item->GetLabel()));
}
void SSortFileItem::ByFile(CFileItemPtr &item)
{
if (!item) return;
CURL url(item->GetPath());
CStdString label;
label.Format("%s %d", url.GetFileNameWithoutPath().c_str(), item->m_lStartOffset);
item->SetSortLabel(label);
}
void SSortFileItem::ByFullPath(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%s %d", item->GetPath(), item->m_lStartOffset);
item->SetSortLabel(label);
}
void SSortFileItem::ByLastPlayed(CFileItemPtr &item)
{
if (!item) return;
if (item->HasVideoInfoTag())
item->SetSortLabel(item->GetVideoInfoTag()->m_lastPlayed);
else if (item->HasMusicInfoTag()) // TODO: No last played info in the fileitem for music
item->SetSortLabel(item->GetMusicInfoTag()->GetTitle());
else
item->SetSortLabel(item->GetLabel());
}
void SSortFileItem::ByDate(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%s %s", item->m_dateTime.GetAsDBDateTime().c_str(), item->GetLabel().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByDateAdded(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%d", item->GetVideoInfoTag()->m_iFileId);
item->SetSortLabel(label);
}
void SSortFileItem::BySize(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%"PRId64, item->m_dwSize);
item->SetSortLabel(label);
}
void SSortFileItem::ByDriveType(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%d %s", item->m_iDriveType, item->GetLabel().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::BySongTitle(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetMusicInfoTag()->GetTitle());
}
void SSortFileItem::BySongTitleNoThe(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(RemoveArticles(item->GetMusicInfoTag()->GetTitle()));
}
void SSortFileItem::BySongAlbum(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label = item->GetMusicInfoTag()->GetAlbum();
else if (item->HasVideoInfoTag())
label = item->GetVideoInfoTag()->m_strAlbum;
CStdString artist;
if (item->HasMusicInfoTag())
artist = item->GetMusicInfoTag()->GetArtist();
else if (item->HasVideoInfoTag())
artist = item->GetVideoInfoTag()->m_strArtist;
label += " " + artist;
if (item->HasMusicInfoTag())
label.AppendFormat(" %i", item->GetMusicInfoTag()->GetTrackAndDiskNumber());
item->SetSortLabel(label);
}
void SSortFileItem::BySongAlbumNoThe(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label = item->GetMusicInfoTag()->GetAlbum();
else if (item->HasVideoInfoTag())
label = item->GetVideoInfoTag()->m_strAlbum;
label = RemoveArticles(label);
CStdString artist;
if (item->HasMusicInfoTag())
artist = item->GetMusicInfoTag()->GetArtist();
else if (item->HasVideoInfoTag())
artist = item->GetVideoInfoTag()->m_strArtist;
label += " " + RemoveArticles(artist);
if (item->HasMusicInfoTag())
label.AppendFormat(" %i", item->GetMusicInfoTag()->GetTrackAndDiskNumber());
item->SetSortLabel(label);
}
void SSortFileItem::BySongArtist(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label = item->GetMusicInfoTag()->GetArtist();
else if (item->HasVideoInfoTag())
label = item->GetVideoInfoTag()->m_strArtist;
if (g_advancedSettings.m_bMusicLibraryAlbumsSortByArtistThenYear)
{
int year = 0;
if (item->HasMusicInfoTag())
year = item->GetMusicInfoTag()->GetYear();
else if (item->HasVideoInfoTag())
year = item->GetVideoInfoTag()->m_iYear;
label.AppendFormat(" %i", year);
}
CStdString album;
if (item->HasMusicInfoTag())
album = item->GetMusicInfoTag()->GetAlbum();
else if (item->HasVideoInfoTag())
album = item->GetVideoInfoTag()->m_strAlbum;
label += " " + album;
if (item->HasMusicInfoTag())
label.AppendFormat(" %i", item->GetMusicInfoTag()->GetTrackAndDiskNumber());
item->SetSortLabel(label);
}
void SSortFileItem::BySongArtistNoThe(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label = item->GetMusicInfoTag()->GetArtist();
else if (item->HasVideoInfoTag())
label = item->GetVideoInfoTag()->m_strArtist;
label = RemoveArticles(label);
if (g_advancedSettings.m_bMusicLibraryAlbumsSortByArtistThenYear)
{
int year = 0;
if (item->HasMusicInfoTag())
year = item->GetMusicInfoTag()->GetYear();
else if (item->HasVideoInfoTag())
year = item->GetVideoInfoTag()->m_iYear;
label.AppendFormat(" %i", year);
}
CStdString album;
if (item->HasMusicInfoTag())
album = item->GetMusicInfoTag()->GetAlbum();
else if (item->HasVideoInfoTag())
album = item->GetVideoInfoTag()->m_strAlbum;
label += " " + RemoveArticles(album);
if (item->HasMusicInfoTag())
label.AppendFormat(" %i", item->GetMusicInfoTag()->GetTrackAndDiskNumber());
item->SetSortLabel(label);
}
void SSortFileItem::BySongTrackNum(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label.Format("%i", item->GetMusicInfoTag()->GetTrackAndDiskNumber());
if (item->HasVideoInfoTag())
label.Format("%i", item->GetVideoInfoTag()->m_iTrack);
item->SetSortLabel(label);
}
void SSortFileItem::BySongDuration(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%i", item->GetMusicInfoTag()->GetDuration());
item->SetSortLabel(label);
}
void SSortFileItem::BySongRating(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%c %s", item->GetMusicInfoTag()->GetRating(), item->GetMusicInfoTag()->GetTitle().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByProgramCount(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%i", item->m_iprogramCount);
item->SetSortLabel(label);
}
void SSortFileItem::ByGenre(CFileItemPtr &item)
{
if (!item) return;
if (item->HasMusicInfoTag())
item->SetSortLabel(item->GetMusicInfoTag()->GetGenre());
else
item->SetSortLabel(item->GetVideoInfoTag()->m_strGenre);
}
void SSortFileItem::ByCountry(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetVideoInfoTag()->m_strCountry);
}
void SSortFileItem::ByYear(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->HasMusicInfoTag())
label.Format("%i %s", item->GetMusicInfoTag()->GetYear(), item->GetLabel().c_str());
else
label.Format("%s %s %i %s", item->GetVideoInfoTag()->m_strPremiered.c_str(), item->GetVideoInfoTag()->m_strFirstAired, item->GetVideoInfoTag()->m_iYear, item->GetLabel().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByMovieTitle(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetVideoInfoTag()->m_strTitle);
}
void SSortFileItem::ByMovieSortTitle(CFileItemPtr &item)
{
if (!item) return;
if (!item->GetVideoInfoTag()->m_strSortTitle.IsEmpty())
item->SetSortLabel(item->GetVideoInfoTag()->m_strSortTitle);
else
item->SetSortLabel(item->GetVideoInfoTag()->m_strTitle);
}
void SSortFileItem::ByMovieSortTitleNoThe(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (!item->GetVideoInfoTag()->m_strSortTitle.IsEmpty())
label = item->GetVideoInfoTag()->m_strSortTitle;
else
label = item->GetVideoInfoTag()->m_strTitle;
item->SetSortLabel(RemoveArticles(label));
}
void SSortFileItem::ByMovieRating(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%f %s", item->GetVideoInfoTag()->m_fRating, item->GetLabel().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByMovieRuntime(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
if (item->GetVideoInfoTag()->m_streamDetails.GetVideoDuration() > 0)
label.Format("%i %s", item->GetVideoInfoTag()->m_streamDetails.GetVideoDuration(), item->GetLabel().c_str());
else
label.Format("%s %s", item->GetVideoInfoTag()->m_strRuntime, item->GetLabel().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByMPAARating(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetVideoInfoTag()->m_strMPAARating + " " + item->GetLabel());
}
void SSortFileItem::ByStudio(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetVideoInfoTag()->m_strStudio);
}
void SSortFileItem::ByStudioNoThe(CFileItemPtr &item)
{
if (!item) return;
CStdString studio = item->GetVideoInfoTag()->m_strStudio;
item->SetSortLabel(RemoveArticles(studio));
}
void SSortFileItem::ByEpisodeNum(CFileItemPtr &item)
{
if (!item) return;
const CVideoInfoTag *tag = item->GetVideoInfoTag();
// we calculate an offset number based on the episode's
// sort season and episode values. in addition
// we include specials 'episode' numbers to get proper
// sorting of multiple specials in a row. each
// of these are given their particular ranges to semi-ensure uniqueness.
// theoretical problem: if a show has > 2^15 specials and two of these are placed
// after each other they will sort backwards. if a show has > 2^32-1 seasons
// or if a season has > 2^16-1 episodes strange things will happen (overflow)
unsigned int num;
if (tag->m_iSpecialSortEpisode > 0)
num = (tag->m_iSpecialSortSeason<<24)+(tag->m_iSpecialSortEpisode<<8)-(128-tag->m_iEpisode);
else
num = (tag->m_iSeason<<24)+(tag->m_iEpisode<<8);
// check filename as there can be duplicates now
CURL file(tag->m_strFileNameAndPath);
CStdString label;
label.Format("%u %s", num, file.GetFileName().c_str());
item->SetSortLabel(label);
}
void SSortFileItem::ByProductionCode(CFileItemPtr &item)
{
if (!item) return;
item->SetSortLabel(item->GetVideoInfoTag()->m_strProductionCode);
}
void SSortFileItem::ByBitrate(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%"PRId64, item->m_dwSize);
item->SetSortLabel(label);
}
void SSortFileItem::ByListeners(CFileItemPtr &item)
{
if (!item) return;
CStdString label;
label.Format("%i", item->GetMusicInfoTag()->GetListeners());
item->SetSortLabel(label);
}