1 /***************************************************************************
2  *   Copyright (C) 2008-2021 by Andrzej Rybczak                            *
3  *   andrzej@rybczak.net                                                   *
4  *                                                                         *
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.                                   *
9  *                                                                         *
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.                          *
14  *                                                                         *
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  ***************************************************************************/
20 
21 #include <algorithm>
22 #include <boost/range/adaptor/reversed.hpp>
23 #include <time.h>
24 
25 #include "enums.h"
26 #include "helpers.h"
27 #include "format_impl.h"
28 #include "screens/playlist.h"
29 #include "statusbar.h"
30 #include "utility/functional.h"
31 
currentSong(const BaseScreen * screen)32 const MPD::Song *currentSong(const BaseScreen *screen)
33 {
34 	const MPD::Song *ptr = nullptr;
35 	const auto *list = dynamic_cast<const SongList *>(screen->activeWindow());
36 	if (list != nullptr)
37 	{
38 		const auto it = list->currentS();
39 		if (it != list->endS())
40 			ptr = it->song();
41 	}
42 	return ptr;
43 }
44 
deleteSelectedSongsFromPlaylist(NC::Menu<MPD::Song> & playlist)45 void deleteSelectedSongsFromPlaylist(NC::Menu<MPD::Song> &playlist)
46 {
47 	selectCurrentIfNoneSelected(playlist);
48 	boost::optional<int> range_end;
49 	Mpd.StartCommandsList();
50 	for (auto &s : boost::adaptors::reverse(playlist))
51 	{
52 		if (s.isSelected())
53 		{
54 			s.setSelected(false);
55 			if (range_end == boost::none)
56 				range_end = s.value().getPosition() + 1;
57 		}
58 		else if (range_end != boost::none)
59 		{
60 			Mpd.DeleteRange(s.value().getPosition() + 1, *range_end);
61 			range_end.reset();
62 		}
63 	}
64 	if (range_end != boost::none)
65 		Mpd.DeleteRange(0, *range_end);
66 	Mpd.CommitCommandsList();
67 }
68 
removeSongFromPlaylist(const SongMenu & playlist,const MPD::Song & s)69 void removeSongFromPlaylist(const SongMenu &playlist, const MPD::Song &s)
70 {
71 	Mpd.StartCommandsList();
72 	for (auto &item : boost::adaptors::reverse(playlist))
73 		if (item.value() == s)
74 			Mpd.Delete(item.value().getPosition());
75 	Mpd.CommitCommandsList();
76 }
77 
addSongToPlaylist(const MPD::Song & s,bool play,int position)78 bool addSongToPlaylist(const MPD::Song &s, bool play, int position)
79 {
80 	bool result = false;
81 	if (Config.space_add_mode == SpaceAddMode::AddRemove
82 	&&  myPlaylist->checkForSong(s)
83 	   )
84 	{
85 		result = true;
86 		if (play)
87 		{
88 			const auto begin = myPlaylist->main().beginV(), end = myPlaylist->main().endV();
89 			auto it = find_map_first(begin, end, s, [](const MPD::Song &found) {
90 				Mpd.PlayID(found.getID());
91 			});
92 			assert(it != end);
93 		}
94 		else
95 			removeSongFromPlaylist(myPlaylist->main(), s);
96 		return result;
97 	}
98 	int id = Mpd.AddSong(s, position);
99 	if (id >= 0)
100 	{
101 		Statusbar::printf("Added to playlist: %s",
102 			Format::stringify<char>(Config.song_status_format, &s)
103 		);
104 		if (play)
105 			Mpd.PlayID(id);
106 		result = true;
107 	}
108 	return result;
109 }
110 
timeFormat(const char * format,time_t t)111 std::string timeFormat(const char *format, time_t t)
112 {
113 	char result[32];
114 	tm tinfo;
115 	localtime_r(&t, &tinfo);
116 	strftime(result, sizeof(result), format, &tinfo);
117 	return result;
118 }
119 
Timestamp(time_t t)120 std::string Timestamp(time_t t)
121 {
122 	char result[32];
123 	tm info;
124 	result[strftime(result, 31, "%x %X", localtime_r(&t, &info))] = 0;
125 	return result;
126 }
127 
Scroller(const std::wstring & str,size_t & pos,size_t width)128 std::wstring Scroller(const std::wstring &str, size_t &pos, size_t width)
129 {
130 	std::wstring s(str);
131 	if (!Config.header_text_scrolling)
132 		return s;
133 	std::wstring result;
134 	size_t len = wideLength(s);
135 
136 	if (len > width)
137 	{
138 		s += L" ** ";
139 		len = 0;
140 		auto b = s.begin(), e = s.end();
141 		for (auto it = b+pos; it < e && len < width; ++it)
142 		{
143 			if ((len += wcwidth(*it)) > width)
144 				break;
145 			result += *it;
146 		}
147 		if (++pos >= s.length())
148 			pos = 0;
149 		for (; len < width; ++b)
150 		{
151 			if ((len += wcwidth(*b)) > width)
152 				break;
153 			result += *b;
154 		}
155 	}
156 	else
157 		result = s;
158 	return result;
159 }
160 
writeCyclicBuffer(const NC::WBuffer & buf,NC::Window & w,size_t & start_pos,size_t width,const std::wstring & separator)161 void writeCyclicBuffer(const NC::WBuffer &buf, NC::Window &w, size_t &start_pos,
162                        size_t width, const std::wstring &separator)
163 {
164 	const auto &s = buf.str();
165 	size_t len = wideLength(s);
166 	if (len > width)
167 	{
168 		len = 0;
169 		const auto &ps = buf.properties();
170 		auto p = ps.begin();
171 
172 		// load attributes from before starting pos
173 		for (; p != ps.end() && p->first < start_pos; ++p)
174 			w << p->second;
175 
176 		auto write_buffer = [&](size_t start) {
177 			for (size_t i = start; i < s.length() && len < width; ++i)
178 			{
179 				for (; p != ps.end() && p->first == i; ++p)
180 					w << p->second;
181 				len += wcwidth(s[i]);
182 				if (len > width)
183 					break;
184 				w << s[i];
185 			}
186 			for (; p != ps.end(); ++p)
187 				w << p->second;
188 			p = ps.begin();
189 		};
190 
191 		write_buffer(start_pos);
192 		size_t i = 0;
193 		if (start_pos > s.length())
194 			i = start_pos - s.length();
195 		for (; i < separator.length() && len < width; ++i)
196 		{
197 			len += wcwidth(separator[i]);
198 			if (len > width)
199 				break;
200 			w << separator[i];
201 		}
202 		write_buffer(0);
203 
204 		++start_pos;
205 		if (start_pos >= s.length() + separator.length())
206 			start_pos = 0;
207 	}
208 	else
209 		w << buf;
210 }
211