1 /***************************************************************************
2 * Copyright (C) 2008-2016 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
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 of the License, or *
8 * (at your option) any later version. *
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 this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
22 #include <boost/date_time/posix_time/posix_time.hpp>
25 #include "curses/menu_impl.h"
29 #include "screens/playlist.h"
30 #include "screens/screen_switcher.h"
33 #include "statusbar.h"
34 #include "helpers/song_iterator_maker.h"
35 #include "utility/comparators.h"
36 #include "utility/functional.h"
39 using Global::MainHeight
;
40 using Global::MainStartY
;
42 namespace ph
= std::placeholders
;
48 std::string
songToString(const MPD::Song
&s
);
49 bool playlistEntryMatcher(const Regex::Regex
&rx
, const MPD::Song
&s
);
54 : m_total_length(0), m_remaining_time(0), m_scroll_begin(0)
55 , m_timer(boost::posix_time::from_time_t(0))
56 , m_reload_total_length(false), m_reload_remaining(false)
58 w
= NC::Menu
<MPD::Song
>(0, MainStartY
, COLS
, MainHeight
, Config
.playlist_display_mode
== DisplayMode::Columns
&& Config
.titles_visibility
? Display::Columns(COLS
) : "", Config
.main_color
, NC::Border());
59 w
.cyclicScrolling(Config
.use_cyclic_scrolling
);
60 w
.centeredCursor(Config
.centered_cursor
);
61 w
.setHighlightColor(Config
.main_highlight_color
);
62 w
.setSelectedPrefix(Config
.selected_item_prefix
);
63 w
.setSelectedSuffix(Config
.selected_item_suffix
);
64 switch (Config
.playlist_display_mode
)
66 case DisplayMode::Classic
:
67 w
.setItemDisplayer(std::bind(
68 Display::Songs
, ph::_1
, std::cref(w
), std::cref(Config
.song_list_format
)
71 case DisplayMode::Columns
:
72 w
.setItemDisplayer(std::bind(
73 Display::SongsInColumns
, ph::_1
, std::cref(w
)
79 void Playlist::switchTo()
81 SwitchTo::execute(this);
86 void Playlist::resize()
88 size_t x_offset
, width
;
89 getWindowResizeParams(x_offset
, width
);
90 w
.resize(width
, MainHeight
);
91 w
.moveTo(x_offset
, MainStartY
);
93 switch (Config
.playlist_display_mode
)
95 case DisplayMode::Columns
:
96 if (Config
.titles_visibility
)
98 w
.setTitle(Display::Columns(w
.getWidth()));
101 case DisplayMode::Classic
:
108 std::wstring
Playlist::title()
110 std::wstring result
= L
"Playlist ";
111 if (Config
.playlist_show_mpd_host
)
114 result
+= ToWString(Mpd
.GetHostname());
117 if (m_reload_total_length
|| m_reload_remaining
)
118 m_stats
= getTotalLength();
119 result
+= Scroller(ToWString(m_stats
), m_scroll_begin
, COLS
-result
.length()-(Config
.design
== Design::Alternative
? 2 : Global::VolumeState
.length()));
123 void Playlist::update()
125 if (w
.isHighlighted()
126 && Config
.playlist_disable_highlight_delay
.time_duration::seconds() > 0
127 && Global::Timer
- m_timer
> Config
.playlist_disable_highlight_delay
)
129 w
.setHighlighting(false);
134 void Playlist::mouseButtonPressed(MEVENT me
)
136 if (!w
.empty() && w
.hasCoords(me
.x
, me
.y
))
138 if (size_t(me
.y
) < w
.size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
141 if (me
.bstate
& BUTTON3_PRESSED
)
142 addItemToPlaylist(true);
145 Screen
<WindowType
>::mouseButtonPressed(me
);
149 /***********************************************************************/
151 bool Playlist::allowsSearching()
156 const std::string
&Playlist::searchConstraint()
158 return m_search_predicate
.constraint();
161 void Playlist::setSearchConstraint(const std::string
&constraint
)
163 m_search_predicate
= Regex::Filter
<MPD::Song
>(
166 playlistEntryMatcher
);
169 void Playlist::clearSearchConstraint()
171 m_search_predicate
.clear();
174 bool Playlist::search(SearchDirection direction
, bool wrap
, bool skip_current
)
176 return ::search(w
, m_search_predicate
, direction
, wrap
, skip_current
);
179 /***********************************************************************/
181 bool Playlist::allowsFiltering()
183 return allowsSearching();
186 std::string
Playlist::currentFilter()
189 if (auto pred
= w
.filterPredicate
<Regex::Filter
<MPD::Song
>>())
190 result
= pred
->constraint();
194 void Playlist::applyFilter(const std::string
&constraint
)
196 if (!constraint
.empty())
198 w
.applyFilter(Regex::Filter
<MPD::Song
>(
201 playlistEntryMatcher
));
207 /***********************************************************************/
209 bool Playlist::itemAvailable()
214 bool Playlist::addItemToPlaylist(bool play
)
217 Mpd
.PlayID(w
.currentV()->getID());
221 std::vector
<MPD::Song
> Playlist::getSelectedSongs()
223 return w
.getSelectedSongs();
226 /***********************************************************************/
228 MPD::Song
Playlist::nowPlayingSong()
231 if (Status::State::player() != MPD::psUnknown
)
233 ScopedUnfilteredMenu
<MPD::Song
> sunfilter(ReapplyFilter::No
, w
);
234 auto sp
= Status::State::currentSongPosition();
235 if (sp
>= 0 && size_t(sp
) < w
.size())
236 s
= w
.at(sp
).value();
241 void Playlist::locateSong(const MPD::Song
&s
)
244 w
.highlight(s
.getPosition());
247 auto cmp
= [](const MPD::Song
&a
, const MPD::Song
&b
) {
248 return a
.getPosition() < b
.getPosition();
250 auto first
= w
.beginV(), last
= w
.endV();
251 auto it
= std::lower_bound(first
, last
, s
, cmp
);
252 if (it
!= last
&& it
->getPosition() == s
.getPosition())
253 w
.highlight(it
- first
);
255 Statusbar::print("Song is filtered out");
259 void Playlist::enableHighlighting()
261 w
.setHighlighting(true);
262 m_timer
= Global::Timer
;
265 std::string
Playlist::getTotalLength()
267 std::ostringstream result
;
269 if (m_reload_total_length
)
272 for (const auto &s
: w
)
273 m_total_length
+= s
.value().getDuration();
274 m_reload_total_length
= false;
276 if (Config
.playlist_show_remaining_time
&& m_reload_remaining
)
278 ScopedUnfilteredMenu
<MPD::Song
> sunfilter(ReapplyFilter::No
, w
);
279 m_remaining_time
= 0;
280 for (size_t i
= Status::State::currentSongPosition(); i
< w
.size(); ++i
)
281 m_remaining_time
+= w
[i
].value().getDuration();
282 m_reload_remaining
= false;
285 result
<< '(' << w
.size() << (w
.size() == 1 ? " item" : " items");
289 ScopedUnfilteredMenu
<MPD::Song
> sunfilter(ReapplyFilter::No
, w
);
290 result
<< " (out of " << w
.size() << ")";
295 result
<< ", length: ";
296 ShowTime(result
, m_total_length
, Config
.playlist_shorten_total_times
);
298 if (Config
.playlist_show_remaining_time
&& m_remaining_time
&& w
.size() > 1)
300 result
<< ", remaining: ";
301 ShowTime(result
, m_remaining_time
, Config
.playlist_shorten_total_times
);
307 void Playlist::setSelectedItemsPriority(int prio
)
309 auto list
= getSelectedOrCurrent(w
.begin(), w
.end(), w
.current());
310 Mpd
.StartCommandsList();
311 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
312 Mpd
.SetPriority((*it
)->value(), prio
);
313 Mpd
.CommitCommandsList();
314 Statusbar::print("Priority set");
317 bool Playlist::checkForSong(const MPD::Song
&s
)
319 return m_song_refs
.find(s
) != m_song_refs
.end();
322 void Playlist::registerSong(const MPD::Song
&s
)
327 void Playlist::unregisterSong(const MPD::Song
&s
)
329 auto it
= m_song_refs
.find(s
);
330 assert(it
!= m_song_refs
.end());
332 m_song_refs
.erase(it
);
339 std::string
songToString(const MPD::Song
&s
)
342 switch (Config
.playlist_display_mode
)
344 case DisplayMode::Classic
:
345 result
= Format::stringify
<char>(Config
.song_list_format
, &s
);
347 case DisplayMode::Columns
:
348 result
= Format::stringify
<char>(Config
.song_columns_mode_format
, &s
);
353 bool playlistEntryMatcher(const Regex::Regex
&rx
, const MPD::Song
&s
)
355 return Regex::search(songToString(s
), rx
);