summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/bindings3
-rw-r--r--doc/config2
-rw-r--r--src/actions.cpp24
-rw-r--r--src/actions.h12
-rw-r--r--src/bindings.cpp1
-rw-r--r--src/global.h2
-rw-r--r--src/help.cpp1
-rw-r--r--src/media_library.cpp181
-rw-r--r--src/media_library.h25
-rw-r--r--src/mpdpp.cpp103
-rw-r--r--src/mpdpp.h36
-rw-r--r--src/mutable_song.h2
-rw-r--r--src/settings.cpp5
-rw-r--r--src/settings.h1
-rw-r--r--src/song.h3
15 files changed, 336 insertions, 65 deletions
diff --git a/doc/bindings b/doc/bindings
index ae443964..861ec216 100644
--- a/doc/bindings
+++ b/doc/bindings
@@ -431,6 +431,9 @@
#def_key "m"
# move_selected_items_up
#
+#def_key "m"
+# toggle_media_library_sort_mode
+#
#def_key "n"
# move_sort_order_down
#
diff --git a/doc/config b/doc/config
index ffc10e1c..8a171747 100644
--- a/doc/config
+++ b/doc/config
@@ -437,6 +437,8 @@
#
#media_library_display_empty_tag = "yes"
#
+#media_library_sort_by_mtime = "no"
+#
#enable_window_title = "yes"
#
##
diff --git a/src/actions.cpp b/src/actions.cpp
index 62c1b128..c181a5c6 100644
--- a/src/actions.cpp
+++ b/src/actions.cpp
@@ -1397,13 +1397,13 @@ void EditLibraryTag::Run()
Statusbar::lock();
Statusbar::put() << NC::fmtBold << tagTypeToString(Config.media_lib_primary_tag) << NC::fmtBoldEnd << ": ";
- std::string new_tag = wFooter->getString(myLibrary->Tags.current().value());
+ std::string new_tag = wFooter->getString(myLibrary->Tags.current().value().tag());
Statusbar::unlock();
- if (!new_tag.empty() && new_tag != myLibrary->Tags.current().value())
+ if (!new_tag.empty() && new_tag != myLibrary->Tags.current().value().tag())
{
Statusbar::msg("Updating tags...");
Mpd.StartSearch(1);
- Mpd.AddSearch(Config.media_lib_primary_tag, myLibrary->Tags.current().value());
+ Mpd.AddSearch(Config.media_lib_primary_tag, myLibrary->Tags.current().value().tag());
MPD::MutableSong::SetFunction set = tagTypeToSetFunction(Config.media_lib_primary_tag);
assert(set);
bool success = true;
@@ -2162,12 +2162,15 @@ void ToggleLibraryTagType::Run()
myLibrary->Tags.setTitle(Config.titles_visibility ? item_type + "s" : "");
myLibrary->Tags.reset();
item_type = lowercase(item_type);
+ std::string and_mtime = Config.media_library_sort_by_mtime ?
+ " and mtime" :
+ "";
if (myLibrary->Columns() == 2)
{
myLibrary->Songs.clear();
myLibrary->Albums.reset();
myLibrary->Albums.clear();
- myLibrary->Albums.setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + ")" : "");
+ myLibrary->Albums.setTitle(Config.titles_visibility ? "Albums (sorted by " + item_type + and_mtime + ")" : "");
myLibrary->Albums.display();
}
else
@@ -2179,6 +2182,16 @@ void ToggleLibraryTagType::Run()
}
}
+bool ToggleMediaLibrarySortMode::canBeRun() const
+{
+ return myScreen == myLibrary;
+}
+
+void ToggleMediaLibrarySortMode::Run()
+{
+ myLibrary->toggleMTimeSort();
+}
+
bool RefetchLyrics::canBeRun() const
{
# ifdef HAVE_CURL_CURL_H
@@ -2295,7 +2308,7 @@ void ShowArtistInfo::Run()
{
assert(!myLibrary->Tags.empty());
assert(Config.media_lib_primary_tag == MPD_TAG_ARTIST);
- artist = myLibrary->Tags.current().value();
+ artist = myLibrary->Tags.current().value().tag();
}
else
{
@@ -2660,6 +2673,7 @@ void populateActions()
insertAction(new AddRandomItems());
insertAction(new ToggleBrowserSortMode());
insertAction(new ToggleLibraryTagType());
+ insertAction(new ToggleMediaLibrarySortMode());
insertAction(new RefetchLyrics());
insertAction(new RefetchArtistInfo());
insertAction(new SetSelectedItemsPriority());
diff --git a/src/actions.h b/src/actions.h
index 92b1ad8d..86615ffc 100644
--- a/src/actions.h
+++ b/src/actions.h
@@ -43,7 +43,7 @@ enum ActionType
aCropMainPlaylist, aCropPlaylist, aClearMainPlaylist, aClearPlaylist, aSortPlaylist, aReversePlaylist,
aApplyFilter, aFind, aFindItemForward, aFindItemBackward, aNextFoundItem,
aPreviousFoundItem, aToggleFindMode, aToggleReplayGainMode, aToggleSpaceMode, aToggleAddMode,
- aToggleMouse, aToggleBitrateVisibility, aAddRandomItems, aToggleBrowserSortMode, aToggleLibraryTagType,
+ aToggleMouse, aToggleBitrateVisibility, aAddRandomItems, aToggleBrowserSortMode, aToggleLibraryTagType, aToggleMediaLibrarySortMode,
aRefetchLyrics, aRefetchArtistInfo, aSetSelectedItemsPriority, aFilterPlaylistOnPriorities,
aShowSongInfo, aShowArtistInfo,
aShowLyrics, aQuit, aNextScreen, aPreviousScreen, aShowHelp, aShowPlaylist, aShowBrowser, aChangeBrowseMode,
@@ -914,6 +914,16 @@ protected:
virtual void Run();
};
+struct ToggleMediaLibrarySortMode : public Action
+{
+ ToggleMediaLibrarySortMode()
+ : Action(aToggleMediaLibrarySortMode, "toggle_media_library_sort_mode") { }
+
+protected:
+ virtual bool canBeRun() const;
+ virtual void Run();
+};
+
struct RefetchLyrics : public Action
{
RefetchLyrics() : Action(aRefetchLyrics, "refetch_lyrics") { }
diff --git a/src/bindings.cpp b/src/bindings.cpp
index 1f089d10..7dfdacd4 100644
--- a/src/bindings.cpp
+++ b/src/bindings.cpp
@@ -529,6 +529,7 @@ void BindingsConfiguration::generateDefaults()
{
bind(k, aMoveSortOrderUp);
bind(k, aMoveSelectedItemsUp);
+ bind(k, aToggleMediaLibrarySortMode);
}
if (notBound(k = stringToKey("n")))
{
diff --git a/src/global.h b/src/global.h
index 2c9b061c..5cc20794 100644
--- a/src/global.h
+++ b/src/global.h
@@ -35,7 +35,7 @@ extern BaseScreen *myScreen;
extern BaseScreen *myLockedScreen;
// points at inactive screen, if locking was enabled and two screens are displayed
-extern BaseScreen *myInactiveScreen;
+extern BaseScreen *myInactiveScreen;
// header window (above main window)
extern NC::Window *wHeader;
diff --git a/src/help.cpp b/src/help.cpp
index 1ea18d43..50fe45cf 100644
--- a/src/help.cpp
+++ b/src/help.cpp
@@ -325,6 +325,7 @@ void Help::GetKeybindings()
# endif // HAVE_TAGLIB_H
KeyDesc(aEditLibraryTag, "Edit tag (left column)/album (middle/right column)");
KeyDesc(aToggleLibraryTagType, "Toggle type of tag used in left column");
+ KeyDesc(aToggleMediaLibrarySortMode, "Toggle sort mode");
KeysSection("Playlist editor");
KeyDesc(aPreviousColumn, "Previous column");
diff --git a/src/media_library.cpp b/src/media_library.cpp
index cd76a9eb..a18e028e 100644
--- a/src/media_library.cpp
+++ b/src/media_library.cpp
@@ -66,12 +66,12 @@ typedef MediaLibrary::SearchConstraints SearchConstraints;
std::string AlbumToString(const SearchConstraints &sc);
std::string SongToString(const MPD::Song &s);
-bool TagEntryMatcher(const Regex &rx, const std::string &tag);
+bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime);
bool AlbumEntryMatcher(const Regex &rx, const NC::Menu<SearchConstraints>::Item &item, bool filter);
bool SongEntryMatcher(const Regex &rx, const MPD::Song &s);
void DisplayAlbums(NC::Menu<SearchConstraints> &menu);
-void DisplayPrimaryTags(NC::Menu<std::string> &menu);
+void DisplayPrimaryTags(NC::Menu<MPD::TagMTime> &menu);
bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b);
@@ -101,14 +101,34 @@ class SortSearchConstraints {
public:
SortSearchConstraints() : m_cmp(std::locale(), Config.ignore_leading_the) { }
bool operator()(const SearchConstraints &a, const SearchConstraints &b) const {
- int result;
- result = m_cmp(a.PrimaryTag, b.PrimaryTag);
- if (result != 0)
- return result < 0;
- result = m_cmp(a.Date, b.Date);
- if (result != 0)
- return result < 0;
- return m_cmp(a.Album, b.Album) < 0;
+ if (Config.media_library_sort_by_mtime)
+ {
+ return a.MTime > b.MTime;
+ }
+ else
+ {
+ int result;
+ result = m_cmp(a.PrimaryTag, b.PrimaryTag);
+ if (result != 0)
+ return result < 0;
+ result = m_cmp(a.Date, b.Date);
+ if (result != 0)
+ return result < 0;
+ return m_cmp(a.Album, b.Album) < 0;
+ }
+ }
+};
+
+class ArtistSorting {
+ LocaleStringComparison m_cmp;
+public:
+ ArtistSorting() : m_cmp(std::locale(), Config.ignore_leading_the) { }
+ bool operator()(const MPD::TagMTime &a,
+ const MPD::TagMTime &b) const {
+ if (Config.media_library_sort_by_mtime)
+ return a.mtime() > b.mtime();
+ else
+ return m_cmp(a.tag(), b.tag()) < 0;
}
};
@@ -123,7 +143,7 @@ MediaLibrary::MediaLibrary()
itsRightColWidth = COLS-COLS/3*2-1;
itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2;
- Tags = NC::Menu<std::string>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::brNone);
+ Tags = NC::Menu<MPD::TagMTime>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::brNone);
Tags.setHighlightColor(Config.active_column_color);
Tags.cyclicScrolling(Config.use_cyclic_scrolling);
Tags.centeredCursor(Config.centered_cursor);
@@ -209,18 +229,74 @@ std::wstring MediaLibrary::title()
return L"Media library";
}
+bool MediaLibrary::hasMTimes()
+{
+ bool has = false;
+ if (hasTwoColumns && !Albums.empty())
+ has = Albums.current().value().hasMTime();
+ else if (!hasTwoColumns && !Tags.empty())
+ has = Tags.current().value().hasMTime();
+ return has;
+}
+
+void MediaLibrary::toggleMTimeSort()
+{
+ Config.media_library_sort_by_mtime = !Config.media_library_sort_by_mtime;
+ if (Config.media_library_sort_by_mtime)
+ Statusbar::msg("Sorting library by: Modification time");
+ else
+ Statusbar::msg("Sorting library by: Name");
+
+ if (!hasMTimes() && Config.media_library_sort_by_mtime)
+ {
+ Tags.clear();
+ Albums.clear();
+ Songs.clear();
+ }
+ else
+ {
+ if (!hasTwoColumns)
+ {
+ std::sort(Tags.beginV(), Tags.endV(), ArtistSorting());
+ Tags.refresh();
+ Albums.clear();
+ Songs.clear();
+ }
+ else
+ {
+ std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints());
+ Albums.refresh();
+ Songs.clear();
+ }
+ }
+
+ if (hasTwoColumns)
+ {
+ if (Config.titles_visibility)
+ {
+ std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag));
+ std::string and_mtime = Config.media_library_sort_by_mtime ?
+ " and mtime" :
+ "";
+ Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")");
+ }
+ }
+ update();
+}
+
void MediaLibrary::update()
{
if (!hasTwoColumns && Tags.reallyEmpty())
{
Albums.clear();
Songs.clear();
- auto list = Mpd.GetList(Config.media_lib_primary_tag);
- std::sort(list.begin(), list.end(),
- LocaleBasedSorting(std::locale(), Config.ignore_leading_the));
+ auto list = Mpd.GetListMTime(Config.media_lib_primary_tag,
+ Config.media_library_sort_by_mtime);
+
+ std::sort(list.begin(), list.end(), ArtistSorting());
for (auto it = list.begin(); it != list.end(); ++it)
{
- if (it->empty() && !Config.media_library_display_empty_tag)
+ if (it->tag().empty() && !Config.media_library_display_empty_tag)
continue;
Tags.addItem(*it);
}
@@ -234,22 +310,26 @@ void MediaLibrary::update()
// and slows down the whole process.
Mpd.BlockIdle(true);
Albums.reset();
- Mpd.StartFieldSearch(MPD_TAG_ALBUM);
- Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value());
- auto albums = Mpd.CommitSearchTags();
- for (auto album = albums.begin(); album != albums.end(); ++album)
+ Mpd.StartFieldSearchMTime(MPD_TAG_ALBUM, Config.media_library_sort_by_mtime);
+ Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value().tag());
+ auto albums = Mpd.CommitSearchTagsMTime();
+ for (auto tagmtime = albums.begin(); tagmtime != albums.end(); ++tagmtime)
{
+ const std::string &album = tagmtime->tag();
+ time_t mtime = tagmtime->mtime();
if (Config.media_library_display_date)
{
Mpd.StartFieldSearch(MPD_TAG_DATE);
- Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value());
- Mpd.AddSearch(MPD_TAG_ALBUM, *album);
+
+ Mpd.AddSearch(Config.media_lib_primary_tag,
+ Tags.current().value().tag());
+ Mpd.AddSearch(MPD_TAG_ALBUM, album);
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
- Albums.addItem(SearchConstraints(*album, *date));
+ Albums.addItem(SearchConstraints(album, *date, mtime));
}
else
- Albums.addItem(SearchConstraints(*album, ""));
+ Albums.addItem(SearchConstraints(album, "", mtime));
}
if (!Albums.empty())
std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints());
@@ -270,30 +350,33 @@ void MediaLibrary::update()
auto artists = Mpd.GetList(Config.media_lib_primary_tag);
for (auto artist = artists.begin(); artist != artists.end(); ++artist)
{
- Mpd.StartFieldSearch(MPD_TAG_ALBUM);
+ Mpd.StartFieldSearchMTime(MPD_TAG_ALBUM, Config.media_library_sort_by_mtime);
Mpd.AddSearch(Config.media_lib_primary_tag, *artist);
- auto albums = Mpd.CommitSearchTags();
- for (auto album = albums.begin(); album != albums.end(); ++album)
+ auto albums = Mpd.CommitSearchTagsMTime();
+ for (auto am = albums.begin(); am != albums.end(); ++am)
{
+ const std::string &album = am->tag();
+ time_t mtime = am->mtime();
if (Config.media_library_display_date)
{
if (Config.media_lib_primary_tag != MPD_TAG_DATE)
{
Mpd.StartFieldSearch(MPD_TAG_DATE);
Mpd.AddSearch(Config.media_lib_primary_tag, *artist);
- Mpd.AddSearch(MPD_TAG_ALBUM, *album);
+ Mpd.AddSearch(MPD_TAG_ALBUM, album);
auto dates = Mpd.CommitSearchTags();
for (auto date = dates.begin(); date != dates.end(); ++date)
- Albums.addItem(SearchConstraints(*artist, *album, *date));
+ Albums.addItem(SearchConstraints(*artist, album, *date, mtime));
}
else
- Albums.addItem(SearchConstraints(*artist, *album, *artist));
+ Albums.addItem(SearchConstraints(*artist, album, *artist, mtime));
}
else
- Albums.addItem(SearchConstraints(*artist, *album, ""));
+ Albums.addItem(SearchConstraints(*artist, album, "", mtime));
}
}
Mpd.BlockIdle(0);
+
if (!Albums.empty())
std::sort(Albums.beginV(), Albums.endV(), SortSearchConstraints());
Albums.refresh();
@@ -311,7 +394,9 @@ void MediaLibrary::update()
Songs.reset();
Mpd.StartSearch(1);
- Mpd.AddSearch(Config.media_lib_primary_tag, hasTwoColumns ? Albums.current().value().PrimaryTag : Tags.current().value());
+ Mpd.AddSearch(Config.media_lib_primary_tag,
+ hasTwoColumns ? Albums.current().value().PrimaryTag :
+ Tags.current().value().tag());
if (Albums.current().value().Date != AllTracksMarker)
{
Mpd.AddSearch(MPD_TAG_ALBUM, Albums.current().value().Album);
@@ -471,7 +556,7 @@ std::string MediaLibrary::currentFilter()
{
std::string filter;
if (isActiveWindow(Tags))
- filter = RegexFilter<std::string>::currentFilter(Tags);
+ filter = RegexFilter<MPD::TagMTime>::currentFilter(Tags);
else if (isActiveWindow(Albums))
filter = RegexItemFilter<SearchConstraints>::currentFilter(Albums);
else if (isActiveWindow(Songs))
@@ -484,7 +569,7 @@ void MediaLibrary::applyFilter(const std::string &filter)
if (isActiveWindow(Tags))
{
Tags.showAll();
- auto rx = RegexFilter<std::string>(filter, Config.regex_type, TagEntryMatcher);
+ auto rx = RegexFilter<MPD::TagMTime>(filter, Config.regex_type, TagEntryMatcher);
Tags.filter(Tags.begin(), Tags.end(), rx);
}
else if (isActiveWindow(Albums))
@@ -514,7 +599,7 @@ bool MediaLibrary::search(const std::string &constraint)
bool result = false;
if (isActiveWindow(Tags))
{
- auto rx = RegexFilter<std::string>(constraint, Config.regex_type, TagEntryMatcher);
+ auto rx = RegexFilter<MPD::TagMTime>(constraint, Config.regex_type, TagEntryMatcher);
result = Tags.search(Tags.begin(), Tags.end(), rx);
}
else if (isActiveWindow(Albums))
@@ -596,10 +681,10 @@ MPD::SongList MediaLibrary::getSelectedSongs()
};
for (auto it = Tags.begin(); it != Tags.end(); ++it)
if (it->isSelected())
- tag_handler(it->value());
+ tag_handler(it->value().tag());
// if no item is selected, add current one
if (result.empty() && !Tags.empty())
- tag_handler(Tags.current().value());
+ tag_handler(Tags.current().value().tag());
}
else if (isActiveWindow(Albums))
{
@@ -612,7 +697,8 @@ MPD::SongList MediaLibrary::getSelectedSongs()
if (hasTwoColumns)
Mpd.AddSearch(Config.media_lib_primary_tag, sc.PrimaryTag);
else
- Mpd.AddSearch(Config.media_lib_primary_tag, Tags.current().value());
+ Mpd.AddSearch(Config.media_lib_primary_tag,
+ Tags.current().value().tag());
Mpd.AddSearch(MPD_TAG_ALBUM, sc.Album);
Mpd.AddSearch(MPD_TAG_DATE, sc.Date);
auto songs = Mpd.CommitSearchSongs();
@@ -726,7 +812,10 @@ void MediaLibrary::toggleColumnsMode()
if (Config.titles_visibility)
{
std::string item_type = lowercase(tagTypeToString(Config.media_lib_primary_tag));
- Albums.setTitle("Albums (sorted by " + item_type + ")");
+ std::string and_mtime = Config.media_library_sort_by_mtime ?
+ " and mtime" :
+ "";
+ Albums.setTitle("Albums (sorted by " + item_type + and_mtime + ")");
}
else
Albums.setTitle("");
@@ -792,11 +881,11 @@ void MediaLibrary::LocateSong(const MPD::Song &s)
Tags.showAll();
if (Tags.empty())
update();
- if (primary_tag != Tags.current().value())
+ if (primary_tag != Tags.current().value().tag())
{
for (size_t i = 0; i < Tags.size(); ++i)
{
- if (primary_tag == Tags[i].value())
+ if (primary_tag == Tags[i].value().tag())
{
Tags.highlight(i);
Albums.clear();
@@ -866,7 +955,7 @@ void MediaLibrary::AddToPlaylist(bool add_n_play)
|| (isActiveWindow(Albums) && Albums.current().value().Date == AllTracksMarker))
{
std::string tag_type = lowercase(tagTypeToString(Config.media_lib_primary_tag));
- Statusbar::msg("Songs with %s = \"%s\" added", tag_type.c_str(), Tags.current().value().c_str());
+ Statusbar::msg("Songs with %s = \"%s\" added", tag_type.c_str(), Tags.current().value().tag().c_str());
}
else if (isActiveWindow(Albums))
Statusbar::msg("Songs from album \"%s\" added", Albums.current().value().Album.c_str());
@@ -915,9 +1004,9 @@ std::string SongToString(const MPD::Song &s)
return s.toString(Config.song_library_format, Config.tags_separator);
}
-bool TagEntryMatcher(const Regex &rx, const std::string &tag)
+bool TagEntryMatcher(const Regex &rx, const MPD::TagMTime &tagmtime)
{
- return rx.match(tag);
+ return rx.match(tagmtime.tag());
}
bool AlbumEntryMatcher(const Regex &rx, const NC::Menu<SearchConstraints>::Item &item, bool filter)
@@ -939,9 +1028,9 @@ void DisplayAlbums(NC::Menu<SearchConstraints> &menu)
menu << AlbumToString(menu.drawn()->value());
}
-void DisplayPrimaryTags(NC::Menu<std::string> &menu)
+void DisplayPrimaryTags(NC::Menu<MPD::TagMTime> &menu)
{
- const std::string &tag = menu.drawn()->value();
+ const std::string &tag = menu.drawn()->value().tag();
if (tag.empty())
menu << Config.empty_tag;
else
@@ -952,7 +1041,7 @@ void DisplayPrimaryTags(NC::Menu<std::string> &menu)
bool SortSongsByTrack(const MPD::Song &a, const MPD::Song &b)
{
- int cmp = a.getDisc().compare(b.getDisc());
+ int cmp = a.getDisc().compare(a.getDisc());
if (cmp != 0)
return cmp < 0;
return a.getTrack() < b.getTrack();
diff --git a/src/media_library.h b/src/media_library.h
index fe956040..68ceb7ae 100644
--- a/src/media_library.h
+++ b/src/media_library.h
@@ -73,21 +73,30 @@ struct MediaLibrary: Screen<NC::Window *>, Filterable, HasColumns, HasSongs, Sea
int Columns();
void LocateSong(const MPD::Song &);
ProxySongList songsProxyList();
-
+
+ // mtimes
+ bool hasMTimes();
+ void toggleMTimeSort();
+
struct SearchConstraints
{
SearchConstraints() { }
- SearchConstraints(const std::string &tag, const std::string &album, const std::string &date)
- : PrimaryTag(tag), Album(album), Date(date) { }
- SearchConstraints(const std::string &album, const std::string &date)
- : Album(album), Date(date) { }
-
+ SearchConstraints(const std::string &tag, const std::string &album, const std::string &date) : PrimaryTag(tag), Album(album), Date(date), MTime(0) { }
+ SearchConstraints(const std::string &album, const std::string &date) : Album(album), Date(date), MTime(0) { }
+ SearchConstraints(const std::string &tag, const std::string &album, const std::string &date, time_t mtime) : PrimaryTag(tag), Album(album), Date(date), MTime(mtime) { }
+ SearchConstraints(const std::string &album, const std::string &date, time_t mtime) : Album(album), Date(date), MTime(mtime) { }
+
std::string PrimaryTag;
std::string Album;
std::string Date;
- };
+ time_t MTime;
- NC::Menu<std::string> Tags;
+ bool operator<(const SearchConstraints &a) const;
+
+ bool hasMTime() { return MTime != 0; }
+ };
+
+ NC::Menu<MPD::TagMTime> Tags;
NC::Menu<SearchConstraints> Albums;
NC::Menu<MPD::Song> Songs;
diff --git a/src/mpdpp.cpp b/src/mpdpp.cpp
index 71233bef..aa099277 100644
--- a/src/mpdpp.cpp
+++ b/src/mpdpp.cpp
@@ -21,6 +21,7 @@
#include <cassert>
#include <cstdlib>
#include <algorithm>
+#include <map>
#include "charset.h"
#include "error.h"
@@ -1140,6 +1141,52 @@ StringList Connection::GetList(mpd_tag_type type)
return result;
}
+TagMTimeList Connection::GetListMTime(mpd_tag_type type, bool get_mtime)
+{
+ TagMTimeList result;
+ if (!itsConnection)
+ return result;
+ assert(!isCommandsListEnabled);
+ GoBusy();
+
+ if (!get_mtime)
+ {
+ mpd_search_db_tags(itsConnection, type);
+ mpd_search_commit(itsConnection);
+ while (mpd_pair *item = mpd_recv_pair_tag(itsConnection, type))
+ {
+ result.push_back(TagMTime(item->value));
+ mpd_return_pair(itsConnection, item);
+ }
+ mpd_response_finish(itsConnection);
+ }
+ else
+ {
+ mpd_send_list_all_meta(itsConnection, "/");
+ std::map<std::string, time_t> max_mtimes;
+ while (mpd_song *s = mpd_recv_song(itsConnection))
+ {
+ Song song(s);
+ const std::string &tag = song.getTag(type);
+ time_t mtime = song.getMTime();
+ auto mt = max_mtimes.find(tag);
+ if (mt == max_mtimes.end())
+ max_mtimes.insert(std::make_pair(tag, mtime));
+ else
+ mt->second = std::max(mt->second, mtime);
+ }
+ mpd_response_finish(itsConnection);
+
+ for (auto it = max_mtimes.begin(); it != max_mtimes.end(); ++it)
+ {
+ result.push_back(TagMTime(it->first, it->second));
+ }
+ }
+
+ GoIdle();
+ return result;
+}
+
void Connection::StartSearch(bool exact_match)
{
if (itsConnection)
@@ -1154,6 +1201,18 @@ void Connection::StartFieldSearch(mpd_tag_type item)
mpd_search_db_tags(itsConnection, item);
}
}
+void Connection::StartFieldSearchMTime(mpd_tag_type item, bool get_mtime)
+{
+ if (itsConnection)
+ {
+ itsSearchedField = item;
+ itsSearchFieldMTime = get_mtime;
+ if (!get_mtime)
+ mpd_search_db_tags(itsConnection, item);
+ else
+ mpd_search_db_songs(itsConnection, 1);
+ }
+}
void Connection::AddSearch(mpd_tag_type item, const std::string &str) const
{
@@ -1211,6 +1270,50 @@ StringList Connection::CommitSearchTags()
return result;
}
+TagMTimeList Connection::CommitSearchTagsMTime()
+{
+ TagMTimeList result;
+ if (!itsConnection)
+ return result;
+
+ assert(!isCommandsListEnabled);
+ GoBusy();
+ mpd_search_commit(itsConnection);
+
+ if (!itsSearchFieldMTime)
+ {
+ while (mpd_pair *tag = mpd_recv_pair_tag(itsConnection, itsSearchedField))
+ {
+ result.push_back(TagMTime(tag->value));
+ mpd_return_pair(itsConnection, tag);
+ }
+ }
+ else
+ {
+ std::map<std::string, time_t> max_mtimes;
+ while (mpd_song *s = mpd_recv_song(itsConnection))
+ {
+ Song song(s);
+ const std::string &tag = song.getTag(itsSearchedField);
+ time_t mtime = song.getMTime();
+ auto mt = max_mtimes.find(tag);
+ if (mt == max_mtimes.end())
+ max_mtimes.insert(std::make_pair(tag, mtime));
+ else
+ mt->second = std::max(mt->second, mtime);
+ }
+
+ for (auto it = max_mtimes.begin(); it != max_mtimes.end(); ++it)
+ {
+ result.push_back(TagMTime(it->first, it->second));
+ }
+ }
+ mpd_response_finish(itsConnection);
+ GoIdle();
+
+ return result;
+}
+
ItemList Connection::GetDirectory(const std::string &path)
{
ItemList result;
diff --git a/src/mpdpp.h b/src/mpdpp.h
index 1ed0966a..c099140d 100644
--- a/src/mpdpp.h
+++ b/src/mpdpp.h
@@ -93,6 +93,35 @@ private:
bool m_enabled;
};
+struct TagMTime
+{
+ TagMTime(const std::string &tag_) : m_tag(tag_), m_mtime(0) { }
+ TagMTime(const std::string &tag_, time_t mtime_) : m_tag(tag_), m_mtime(mtime_) { }
+
+ const std::string &tag() const { return m_tag; }
+ time_t mtime() const { return m_mtime; }
+
+ void set_mtime(time_t mtime_)
+ {
+ m_mtime = mtime_;
+ }
+
+ void set_tag(std::string tag_)
+ {
+ m_tag = tag_;
+ }
+
+ bool hasMTime()
+ {
+ return (m_mtime != 0);
+ }
+
+private:
+ std::string m_tag;
+ time_t m_mtime;
+};
+
+typedef std::vector<TagMTime> TagMTimeList;
typedef std::vector<Item> ItemList;
typedef std::vector<std::string> StringList;
typedef std::vector<Output> OutputList;
@@ -213,14 +242,17 @@ public:
void StartSearch(bool);
void StartFieldSearch(mpd_tag_type);
+ void StartFieldSearchMTime(mpd_tag_type, bool);
void AddSearch(mpd_tag_type, const std::string &) const;
void AddSearchAny(const std::string &str) const;
void AddSearchURI(const std::string &str) const;
SongList CommitSearchSongs();
StringList CommitSearchTags();
+ TagMTimeList CommitSearchTagsMTime();
StringList GetPlaylists();
StringList GetList(mpd_tag_type);
+ TagMTimeList GetListMTime(mpd_tag_type, bool);
ItemList GetDirectory(const std::string &);
SongList GetDirectoryRecursive(const std::string &);
SongList GetSongs(const std::string &);
@@ -240,7 +272,7 @@ private:
int GoBusy();
int CheckForErrors();
-
+
mpd_connection *itsConnection;
bool isCommandsListEnabled;
@@ -272,6 +304,7 @@ private:
void *itsErrorHandlerUserdata;
mpd_tag_type itsSearchedField;
+ bool itsSearchFieldMTime;
};
}
@@ -279,4 +312,3 @@ private:
extern MPD::Connection Mpd;
#endif
-
diff --git a/src/mutable_song.h b/src/mutable_song.h
index b73bb479..f76a2e9b 100644
--- a/src/mutable_song.h
+++ b/src/mutable_song.h
@@ -89,7 +89,7 @@ private:
};
void replaceTag(mpd_tag_type tag_type, std::string &&orig_value,
- const std::string &value, unsigned idx);
+ const std::string &value, unsigned idx);
template <typename F>
std::string getTag(mpd_tag_type tag_type, F orig_value, unsigned idx) const {
diff --git a/src/settings.cpp b/src/settings.cpp
index bc61401a..f5a0dc3e 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -239,6 +239,7 @@ void Configuration::SetDefaults()
new_design = false;
visualizer_use_wave = true;
visualizer_in_stereo = false;
+ media_library_sort_by_mtime = false;
tag_editor_extended_numeration = false;
media_library_display_date = true;
media_library_display_empty_tag = true;
@@ -953,6 +954,10 @@ void Configuration::Read()
if (!v.empty())
media_lib_primary_tag = charToTagType(v[0]);
}
+ else if (name == "media_library_sort_by_mtime")
+ {
+ media_library_sort_by_mtime = v == "yes";
+ }
else
std::cout << "Unknown option: " << name << ", ignoring.\n";
}
diff --git a/src/settings.h b/src/settings.h
index 5ee7c9f5..7ee74956 100644
--- a/src/settings.h
+++ b/src/settings.h
@@ -177,6 +177,7 @@ struct Configuration
bool new_design;
bool visualizer_use_wave;
bool visualizer_in_stereo;
+ bool media_library_sort_by_mtime;
bool tag_editor_extended_numeration;
bool media_library_display_date;
bool media_library_display_empty_tag;
diff --git a/src/song.h b/src/song.h
index ada69b93..16a99c7f 100644
--- a/src/song.h
+++ b/src/song.h
@@ -82,8 +82,9 @@ struct Song
static const char FormatEscapeCharacter = 1;
+ const char *getTag(mpd_tag_type type, unsigned idx = 0) const;
+
private:
- const char *getTag(mpd_tag_type type, unsigned idx) const;
std::string ParseFormat(std::string::const_iterator &it, const std::string &tags_separator,
const std::string &escape_chars) const;