1 /***************************************************************************
2 * Copyright (C) 2008-2014 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/bind.hpp>
30 #include "regex_filter.h"
31 #include "screen_switcher.h"
34 #include "statusbar.h"
35 #include "utility/comparators.h"
38 using Global::MainHeight
;
39 using Global::MainStartY
;
45 std::string
songToString(const MPD::Song
&s
);
46 bool playlistEntryMatcher(const boost::regex
&rx
, const MPD::Song
&s
);
51 : m_total_length(0), m_remaining_time(0), m_scroll_begin(0)
52 , m_reload_total_length(false), m_reload_remaining(false)
54 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::None
);
55 w
.cyclicScrolling(Config
.use_cyclic_scrolling
);
56 w
.centeredCursor(Config
.centered_cursor
);
57 w
.setHighlightColor(Config
.main_highlight_color
);
58 w
.setSelectedPrefix(Config
.selected_item_prefix
);
59 w
.setSelectedSuffix(Config
.selected_item_suffix
);
60 switch (Config
.playlist_display_mode
)
62 case DisplayMode::Classic
:
63 w
.setItemDisplayer(boost::bind(Display::Songs
, _1
, proxySongList(), Config
.song_list_format
));
65 case DisplayMode::Columns
:
66 w
.setItemDisplayer(boost::bind(Display::SongsInColumns
, _1
, proxySongList()));
71 void Playlist::switchTo()
73 SwitchTo::execute(this);
79 void Playlist::resize()
81 size_t x_offset
, width
;
82 getWindowResizeParams(x_offset
, width
);
83 w
.resize(width
, MainHeight
);
84 w
.moveTo(x_offset
, MainStartY
);
86 switch (Config
.playlist_display_mode
)
88 case DisplayMode::Columns
:
89 if (Config
.titles_visibility
)
91 w
.setTitle(Display::Columns(w
.getWidth()));
94 case DisplayMode::Classic
:
101 std::wstring
Playlist::title()
103 std::wstring result
= L
"Playlist ";
104 if (m_reload_total_length
|| m_reload_remaining
)
105 m_stats
= getTotalLength();
106 result
+= Scroller(ToWString(m_stats
), m_scroll_begin
, COLS
-result
.length()-(Config
.design
== Design::Alternative
? 2 : Global::VolumeState
.length()));
110 void Playlist::update()
112 if (w
.isHighlighted()
113 && Config
.playlist_disable_highlight_delay
.time_duration::seconds() > 0
114 && Global::Timer
- m_timer
> Config
.playlist_disable_highlight_delay
)
116 w
.setHighlighting(false);
121 void Playlist::enterPressed()
124 Mpd
.PlayID(w
.current().value().getID());
127 void Playlist::spacePressed()
131 w
.current().setSelected(!w
.current().isSelected());
132 w
.scroll(NC::Scroll::Down
);
136 void Playlist::mouseButtonPressed(MEVENT me
)
138 if (!w
.empty() && w
.hasCoords(me
.x
, me
.y
))
140 if (size_t(me
.y
) < w
.size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
143 if (me
.bstate
& BUTTON3_PRESSED
)
147 Screen
<WindowType
>::mouseButtonPressed(me
);
151 /***********************************************************************/
153 bool Playlist::allowsFiltering()
158 std::string
Playlist::currentFilter()
160 return RegexFilter
<MPD::Song
>::currentFilter(w
);
163 void Playlist::applyFilter(const std::string
&filter
)
168 w
.clearFilterResults();
174 auto rx
= RegexFilter
<MPD::Song
>(
175 boost::regex(filter
, Config
.regex_type
), playlistEntryMatcher
);
176 w
.filter(w
.begin(), w
.end(), rx
);
178 catch (boost::bad_expression
&) { }
181 /***********************************************************************/
183 bool Playlist::allowsSearching()
188 bool Playlist::search(const std::string
&constraint
)
190 if (constraint
.empty())
192 w
.clearSearchResults();
197 auto rx
= RegexFilter
<MPD::Song
>(
198 boost::regex(constraint
, Config
.regex_type
), playlistEntryMatcher
);
199 return w
.search(w
.begin(), w
.end(), rx
);
201 catch (boost::bad_expression
&)
207 void Playlist::nextFound(bool wrap
)
212 void Playlist::prevFound(bool wrap
)
217 /***********************************************************************/
219 ProxySongList
Playlist::proxySongList()
221 return ProxySongList(w
, [](NC::Menu
<MPD::Song
>::Item
&item
) {
222 return &item
.value();
226 bool Playlist::allowsSelection()
231 void Playlist::reverseSelection()
233 reverseSelectionHelper(w
.begin(), w
.end());
236 MPD::SongList
Playlist::getSelectedSongs()
238 MPD::SongList result
;
239 for (auto it
= w
.begin(); it
!= w
.end(); ++it
)
240 if (it
->isSelected())
241 result
.push_back(it
->value());
242 if (result
.empty() && !w
.empty())
243 result
.push_back(w
.current().value());
247 /***********************************************************************/
249 MPD::Song
Playlist::nowPlayingSong()
252 if (Status::get().playerState() != MPD::psStop
)
253 withUnfilteredMenu(w
, [this, &s
]() {
254 s
= w
.at(Status::get().currentSongPosition()).value();
259 bool Playlist::isFiltered()
263 Statusbar::print("Function currently unavailable due to filtered playlist");
269 void Playlist::Reverse()
271 Statusbar::print("Reversing playlist order...");
272 auto begin
= w
.begin(), end
= w
.end();
273 std::tie(begin
, end
) = getSelectedRange(begin
, end
);
274 Mpd
.StartCommandsList();
275 for (--end
; begin
< end
; ++begin
, --end
)
276 Mpd
.Swap(begin
->value().getPosition(), end
->value().getPosition());
277 Mpd
.CommitCommandsList();
278 Statusbar::print("Playlist reversed");
281 void Playlist::EnableHighlighting()
283 w
.setHighlighting(true);
284 m_timer
= Global::Timer
;
287 std::string
Playlist::getTotalLength()
289 std::ostringstream result
;
291 if (m_reload_total_length
)
294 for (const auto &s
: w
)
295 m_total_length
+= s
.value().getDuration();
296 m_reload_total_length
= false;
298 if (Config
.playlist_show_remaining_time
&& m_reload_remaining
&& !w
.isFiltered())
300 m_remaining_time
= 0;
301 for (size_t i
= Status::get().currentSongPosition(); i
< w
.size(); ++i
)
302 m_remaining_time
+= w
[i
].value().getDuration();
303 m_reload_remaining
= false;
306 result
<< '(' << w
.size() << (w
.size() == 1 ? " item" : " items");
311 size_t real_size
= w
.size();
313 if (w
.size() != real_size
)
314 result
<< " (out of " << real_size
<< ")";
319 result
<< ", length: ";
320 ShowTime(result
, m_total_length
, Config
.playlist_shorten_total_times
);
322 if (Config
.playlist_show_remaining_time
&& m_remaining_time
&& !w
.isFiltered() && w
.size() > 1)
324 result
<< " :: remaining: ";
325 ShowTime(result
, m_remaining_time
, Config
.playlist_shorten_total_times
);
331 void Playlist::SetSelectedItemsPriority(int prio
)
333 auto list
= getSelectedOrCurrent(w
.begin(), w
.end(), w
.currentI());
334 Mpd
.StartCommandsList();
335 for (auto it
= list
.begin(); it
!= list
.end(); ++it
)
336 Mpd
.SetPriority((*it
)->value(), prio
);
337 Mpd
.CommitCommandsList();
338 Statusbar::print("Priority set");
341 bool Playlist::checkForSong(const MPD::Song
&s
)
343 return m_song_refs
.find(s
) != m_song_refs
.end();
346 void Playlist::registerSong(const MPD::Song
&s
)
351 void Playlist::unregisterSong(const MPD::Song
&s
)
353 auto it
= m_song_refs
.find(s
);
354 assert(it
!= m_song_refs
.end());
356 m_song_refs
.erase(it
);
363 std::string
songToString(const MPD::Song
&s
)
366 switch (Config
.playlist_display_mode
)
368 case DisplayMode::Classic
:
369 result
= s
.toString(Config
.song_list_format_dollar_free
, Config
.tags_separator
);
371 case DisplayMode::Columns
:
372 result
= s
.toString(Config
.song_in_columns_to_string_format
, Config
.tags_separator
);
377 bool playlistEntryMatcher(const boost::regex
&rx
, const MPD::Song
&s
)
379 return boost::regex_search(songToString(s
), rx
);