diff options
-rw-r--r-- | doc/bindings | 3 | ||||
-rw-r--r-- | doc/config | 2 | ||||
-rw-r--r-- | src/actions.cpp | 24 | ||||
-rw-r--r-- | src/actions.h | 12 | ||||
-rw-r--r-- | src/bindings.cpp | 1 | ||||
-rw-r--r-- | src/global.h | 2 | ||||
-rw-r--r-- | src/help.cpp | 1 | ||||
-rw-r--r-- | src/media_library.cpp | 181 | ||||
-rw-r--r-- | src/media_library.h | 25 | ||||
-rw-r--r-- | src/mpdpp.cpp | 103 | ||||
-rw-r--r-- | src/mpdpp.h | 36 | ||||
-rw-r--r-- | src/mutable_song.h | 2 | ||||
-rw-r--r-- | src/settings.cpp | 5 | ||||
-rw-r--r-- | src/settings.h | 1 | ||||
-rw-r--r-- | src/song.h | 3 |
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 # @@ -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; @@ -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; |