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/lambda/bind.hpp>
23 #include <boost/date_time/posix_time/posix_time.hpp>
31 #include "playlist_editor.h"
32 #include "menu_impl.h"
35 #include "statusbar.h"
36 #include "tag_editor.h"
37 #include "helpers/song_iterator_maker.h"
38 #include "utility/functional.h"
39 #include "utility/comparators.h"
41 #include "screen_switcher.h"
43 using Global::MainHeight
;
44 using Global::MainStartY
;
46 namespace ph
= std::placeholders
;
48 PlaylistEditor
*myPlaylistEditor
;
52 size_t LeftColumnStartX
;
53 size_t LeftColumnWidth
;
54 size_t RightColumnStartX
;
55 size_t RightColumnWidth
;
57 std::string
SongToString(const MPD::Song
&s
);
58 bool PlaylistEntryMatcher(const Regex::Regex
&rx
, const MPD::Playlist
&playlist
);
59 bool SongEntryMatcher(const Regex::Regex
&rx
, const MPD::Song
&s
);
63 PlaylistEditor::PlaylistEditor()
64 : m_timer(boost::posix_time::from_time_t(0))
65 , m_window_timeout(Config
.data_fetching_delay
? 250 : BaseScreen::defaultWindowTimeout
)
66 , m_fetching_delay(boost::posix_time::milliseconds(Config
.data_fetching_delay
? 250 : -1))
68 LeftColumnWidth
= COLS
/3-1;
69 RightColumnStartX
= LeftColumnWidth
+1;
70 RightColumnWidth
= COLS
-LeftColumnWidth
-1;
72 Playlists
= NC::Menu
<MPD::Playlist
>(0, MainStartY
, LeftColumnWidth
, MainHeight
, Config
.titles_visibility
? "Playlists" : "", Config
.main_color
, NC::Border());
73 Playlists
.setHighlightColor(Config
.active_column_color
);
74 Playlists
.cyclicScrolling(Config
.use_cyclic_scrolling
);
75 Playlists
.centeredCursor(Config
.centered_cursor
);
76 Playlists
.setSelectedPrefix(Config
.selected_item_prefix
);
77 Playlists
.setSelectedSuffix(Config
.selected_item_suffix
);
78 Playlists
.setItemDisplayer([](NC::Menu
<MPD::Playlist
> &menu
) {
79 menu
<< Charset::utf8ToLocale(menu
.drawn()->value().path());
82 Content
= NC::Menu
<MPD::Song
>(RightColumnStartX
, MainStartY
, RightColumnWidth
, MainHeight
, Config
.titles_visibility
? "Playlist content" : "", Config
.main_color
, NC::Border());
83 Content
.setHighlightColor(Config
.main_highlight_color
);
84 Content
.cyclicScrolling(Config
.use_cyclic_scrolling
);
85 Content
.centeredCursor(Config
.centered_cursor
);
86 Content
.setSelectedPrefix(Config
.selected_item_prefix
);
87 Content
.setSelectedSuffix(Config
.selected_item_suffix
);
88 switch (Config
.playlist_editor_display_mode
)
90 case DisplayMode::Classic
:
91 Content
.setItemDisplayer(std::bind(
92 Display::Songs
, ph::_1
, std::cref(Content
), std::cref(Config
.song_list_format
)
95 case DisplayMode::Columns
:
96 Content
.setItemDisplayer(std::bind(
97 Display::SongsInColumns
, ph::_1
, std::cref(Content
)
105 void PlaylistEditor::resize()
107 size_t x_offset
, width
;
108 getWindowResizeParams(x_offset
, width
);
110 LeftColumnStartX
= x_offset
;
111 LeftColumnWidth
= width
/3-1;
112 RightColumnStartX
= LeftColumnStartX
+LeftColumnWidth
+1;
113 RightColumnWidth
= width
-LeftColumnWidth
-1;
115 Playlists
.resize(LeftColumnWidth
, MainHeight
);
116 Content
.resize(RightColumnWidth
, MainHeight
);
118 Playlists
.moveTo(LeftColumnStartX
, MainStartY
);
119 Content
.moveTo(RightColumnStartX
, MainStartY
);
124 std::wstring
PlaylistEditor::title()
126 return L
"Playlist editor";
129 void PlaylistEditor::refresh()
132 drawSeparator(RightColumnStartX
-1);
136 void PlaylistEditor::switchTo()
138 SwitchTo::execute(this);
139 markSongsInPlaylist(Content
);
144 void PlaylistEditor::update()
146 if (Playlists
.empty() || m_playlists_update_requested
)
148 m_playlists_update_requested
= false;
150 for (MPD::PlaylistIterator it
= Mpd
.GetPlaylists(), end
; it
!= end
; ++it
, ++idx
)
152 if (idx
< Playlists
.size())
153 Playlists
[idx
].value() = std::move(*it
);
155 Playlists
.addItem(std::move(*it
));
157 if (idx
< Playlists
.size())
158 Playlists
.resizeList(idx
);
159 std::sort(Playlists
.beginV(), Playlists
.endV(),
160 LocaleBasedSorting(std::locale(), Config
.ignore_leading_the
));
164 if ((Content
.empty() && Global::Timer
- m_timer
> m_fetching_delay
)
165 || m_content_update_requested
)
167 m_content_update_requested
= false;
168 if (Playlists
.empty())
173 MPD::SongIterator s
= Mpd
.GetPlaylistContent(Playlists
.current()->value().path()), end
;
174 for (; s
!= end
; ++s
, ++idx
)
176 bool in_playlist
= myPlaylist
->checkForSong(*s
);
177 if (idx
< Content
.size())
179 Content
[idx
].setBold(in_playlist
);
180 Content
[idx
].value() = std::move(*s
);
184 auto properties
= NC::List::Properties::Selectable
;
186 properties
|= NC::List::Properties::Bold
;
187 Content
.addItem(std::move(*s
), properties
);
190 if (idx
< Content
.size())
191 Content
.resizeList(idx
);
193 if (Config
.titles_visibility
)
195 wtitle
= (boost::format("Playlist content (%1%) %2%")
196 % boost::lexical_cast
<std::string
>(Content
.size())
197 % (Content
.size() == 1 ? "item" : "items")
199 wtitle
.resize(Content
.getWidth());
201 Content
.setTitle(wtitle
);
206 if (isActiveWindow(Content
) && Content
.empty())
208 Content
.setHighlightColor(Config
.main_highlight_color
);
209 Playlists
.setHighlightColor(Config
.active_column_color
);
213 if (Playlists
.empty() && Content
.empty())
215 Content
.Window::clear();
216 Content
.Window::display();
220 int PlaylistEditor::windowTimeout()
223 return m_window_timeout
;
225 return Screen
<WindowType
>::windowTimeout();
228 void PlaylistEditor::enterPressed()
230 addItemToPlaylist(true);
233 void PlaylistEditor::mouseButtonPressed(MEVENT me
)
235 if (!Playlists
.empty() && Playlists
.hasCoords(me
.x
, me
.y
))
237 if (!isActiveWindow(Playlists
))
239 if (previousColumnAvailable())
244 if (size_t(me
.y
) < Playlists
.size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
246 Playlists
.Goto(me
.y
);
247 if (me
.bstate
& BUTTON3_PRESSED
)
251 Screen
<WindowType
>::mouseButtonPressed(me
);
254 else if (!Content
.empty() && Content
.hasCoords(me
.x
, me
.y
))
256 if (!isActiveWindow(Content
))
258 if (nextColumnAvailable())
263 if (size_t(me
.y
) < Content
.size() && (me
.bstate
& (BUTTON1_PRESSED
| BUTTON3_PRESSED
)))
266 if (me
.bstate
& BUTTON1_PRESSED
)
272 Screen
<WindowType
>::mouseButtonPressed(me
);
276 /***********************************************************************/
278 bool PlaylistEditor::allowsSearching()
283 void PlaylistEditor::setSearchConstraint(const std::string
&constraint
)
285 if (isActiveWindow(Playlists
))
287 m_playlists_search_predicate
= Regex::Filter
<MPD::Playlist
>(
288 Regex::make(constraint
, Config
.regex_type
),
292 else if (isActiveWindow(Content
))
294 m_content_search_predicate
= Regex::Filter
<MPD::Song
>(
295 Regex::make(constraint
, Config
.regex_type
),
301 void PlaylistEditor::clearConstraint()
303 if (isActiveWindow(Playlists
))
304 m_playlists_search_predicate
.clear();
305 else if (isActiveWindow(Content
))
306 m_content_search_predicate
.clear();
309 bool PlaylistEditor::find(SearchDirection direction
, bool wrap
, bool skip_current
)
312 if (isActiveWindow(Playlists
))
313 result
= search(Playlists
, m_playlists_search_predicate
, direction
, wrap
, skip_current
);
314 else if (isActiveWindow(Content
))
315 result
= search(Content
, m_content_search_predicate
, direction
, wrap
, skip_current
);
319 /***********************************************************************/
321 bool PlaylistEditor::addItemToPlaylist()
323 return addItemToPlaylist(false);
326 std::vector
<MPD::Song
> PlaylistEditor::getSelectedSongs()
328 std::vector
<MPD::Song
> result
;
329 if (isActiveWindow(Playlists
))
331 bool any_selected
= false;
332 for (auto &e
: Playlists
)
338 std::make_move_iterator(Mpd
.GetPlaylistContent(e
.value().path())),
339 std::make_move_iterator(MPD::SongIterator()),
340 std::back_inserter(result
)
344 // if no item is selected, add songs from right column
345 if (!any_selected
&& !Playlists
.empty())
348 std::make_move_iterator(Mpd
.GetPlaylistContent(Playlists
.current()->value().path())),
349 std::make_move_iterator(MPD::SongIterator()),
350 std::back_inserter(result
)
354 else if (isActiveWindow(Content
))
355 result
= Content
.getSelectedSongs();
359 /***********************************************************************/
361 bool PlaylistEditor::previousColumnAvailable()
363 if (isActiveWindow(Content
))
365 if (!Playlists
.empty())
371 void PlaylistEditor::previousColumn()
373 if (isActiveWindow(Content
))
375 Content
.setHighlightColor(Config
.main_highlight_color
);
378 Playlists
.setHighlightColor(Config
.active_column_color
);
382 bool PlaylistEditor::nextColumnAvailable()
384 if (isActiveWindow(Playlists
))
386 if (!Content
.empty())
392 void PlaylistEditor::nextColumn()
394 if (isActiveWindow(Playlists
))
396 Playlists
.setHighlightColor(Config
.main_highlight_color
);
399 Content
.setHighlightColor(Config
.active_column_color
);
403 /***********************************************************************/
405 void PlaylistEditor::updateTimer()
407 m_timer
= Global::Timer
;
410 void PlaylistEditor::Locate(const MPD::Playlist
&playlist
)
413 auto begin
= Playlists
.beginV(), end
= Playlists
.endV();
414 auto it
= std::find(begin
, end
, playlist
);
417 Playlists
.highlight(it
-begin
);
423 bool PlaylistEditor::addItemToPlaylist(bool play
)
426 if (isActiveWindow(Playlists
) && !Playlists
.empty())
428 std::vector
<MPD::Song
> list(
429 std::make_move_iterator(Mpd
.GetPlaylistContent(Playlists
.current()->value().path())),
430 std::make_move_iterator(MPD::SongIterator())
432 result
= addSongsToPlaylist(list
.begin(), list
.end(), play
, -1);
433 Statusbar::printf("Playlist \"%1%\" loaded%2%",
434 Playlists
.current()->value().path(), withErrors(result
)
437 else if (isActiveWindow(Content
) && !Content
.empty())
438 result
= addSongToPlaylist(Content
.current()->value(), play
);
444 std::string
SongToString(const MPD::Song
&s
)
447 switch (Config
.playlist_display_mode
)
449 case DisplayMode::Classic
:
450 result
= Format::stringify
<char>(Config
.song_list_format
, &s
);
452 case DisplayMode::Columns
:
453 result
= Format::stringify
<char>(Config
.song_columns_mode_format
, &s
);
459 bool PlaylistEntryMatcher(const Regex::Regex
&rx
, const MPD::Playlist
&playlist
)
461 return Regex::search(playlist
.path(), rx
);
464 bool SongEntryMatcher(const Regex::Regex
&rx
, const MPD::Song
&s
)
466 return Regex::search(SongToString(s
), rx
);