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 "global.h"
22 #include "settings.h"
23 #include "status.h"
24 #include "statusbar.h"
25 #include "bindings.h"
26 #include "screens/playlist.h"
27 #include "utility/wide_string.h"
28 
29 using Global::wFooter;
30 
31 namespace {
32 
33 bool progressbar_block_update = false;
34 
35 boost::posix_time::ptime statusbar_lock_time;
36 boost::posix_time::seconds statusbar_lock_delay(-1);
37 
38 bool statusbar_block_update = false;
39 bool statusbar_allow_unlock = true;
40 
41 }
42 
ScopedLock()43 Progressbar::ScopedLock::ScopedLock() noexcept
44 {
45 	progressbar_block_update = true;
46 }
47 
~ScopedLock()48 Progressbar::ScopedLock::~ScopedLock() noexcept
49 {
50 	progressbar_block_update = false;
51 }
52 
isUnlocked()53 bool Progressbar::isUnlocked()
54 {
55 	return !progressbar_block_update;
56 }
57 
draw(unsigned int elapsed,unsigned int time)58 void Progressbar::draw(unsigned int elapsed, unsigned int time)
59 {
60 	unsigned pb_width = wFooter->getWidth();
61 	unsigned howlong = time ? pb_width*elapsed/time : 0;
62 	*wFooter << Config.progressbar_color;
63 	if (Config.progressbar[2] != '\0')
64 	{
65 		wFooter->goToXY(0, 0);
66 		for (unsigned i = 0; i < pb_width; ++i)
67 			*wFooter << Config.progressbar[2];
68 		wFooter->goToXY(0, 0);
69 	}
70 	else
71 		mvwhline(wFooter->raw(), 0, 0, 0, pb_width);
72 	*wFooter << NC::FormattedColor::End<>(Config.progressbar_color);
73 	if (time)
74 	{
75 		*wFooter << Config.progressbar_elapsed_color;
76 		pb_width = std::min(size_t(howlong), wFooter->getWidth());
77 		for (unsigned i = 0; i < pb_width; ++i)
78 			*wFooter << Config.progressbar[0];
79 		if (howlong < wFooter->getWidth())
80 			*wFooter << Config.progressbar[1];
81 		*wFooter << NC::FormattedColor::End<>(Config.progressbar_elapsed_color);
82 	}
83 }
84 
ScopedLock()85 Statusbar::ScopedLock::ScopedLock() noexcept
86 {
87 	// lock
88 	if (Config.statusbar_visibility)
89 		statusbar_block_update = true;
90 	else
91 		progressbar_block_update = true;
92 	statusbar_allow_unlock = false;
93 }
94 
~ScopedLock()95 Statusbar::ScopedLock::~ScopedLock() noexcept
96 {
97 	// unlock
98 	statusbar_allow_unlock = true;
99 	if (statusbar_lock_delay.is_negative())
100 	{
101 		if (Config.statusbar_visibility)
102 			statusbar_block_update = false;
103 		else
104 			progressbar_block_update = false;
105 	}
106 	if (Status::State::player() == MPD::psStop)
107 	{
108 		switch (Config.design)
109 		{
110 			case Design::Classic:
111 				put(); // clear statusbar
112 				break;
113 			case Design::Alternative:
114 				Progressbar::draw(Status::State::elapsedTime(), Status::State::totalTime());
115 				break;
116 		}
117 		wFooter->refresh();
118 	}
119 }
120 
isUnlocked()121 bool Statusbar::isUnlocked()
122 {
123 	return !statusbar_block_update;
124 }
125 
tryRedraw()126 void Statusbar::tryRedraw()
127 {
128 	using Global::Timer;
129 	if (statusbar_lock_delay > boost::posix_time::seconds(0)
130 	&&  Timer - statusbar_lock_time > statusbar_lock_delay)
131 	{
132 		statusbar_lock_delay = boost::posix_time::seconds(-1);
133 
134 		if (Config.statusbar_visibility)
135 			statusbar_block_update = !statusbar_allow_unlock;
136 		else
137 			progressbar_block_update = !statusbar_allow_unlock;
138 
139 		if (!statusbar_block_update && !progressbar_block_update)
140 		{
141 			switch (Config.design)
142 			{
143 				case Design::Classic:
144 					switch (Status::State::player())
145 					{
146 						case MPD::psUnknown:
147 						case MPD::psStop:
148 							put(); // clear statusbar
149 							break;
150 						case MPD::psPlay:
151 						case MPD::psPause:
152 							Status::Changes::elapsedTime(false);
153 						break;
154 					}
155 					break;
156 				case Design::Alternative:
157 					Progressbar::draw(Status::State::elapsedTime(), Status::State::totalTime());
158 					break;
159 			}
160 			wFooter->refresh();
161 		}
162 	}
163 }
164 
put()165 NC::Window &Statusbar::put()
166 {
167 	*wFooter << NC::XY(0, Config.statusbar_visibility ? 1 : 0) << NC::TermManip::ClearToEOL;
168 	return *wFooter;
169 }
170 
print(int delay,const std::string & message)171 void Statusbar::print(int delay, const std::string &message)
172 {
173 	if (statusbar_allow_unlock)
174 	{
175         if(delay)
176         {
177             statusbar_lock_time = Global::Timer;
178             statusbar_lock_delay = boost::posix_time::seconds(delay);
179             if (Config.statusbar_visibility)
180                 statusbar_block_update = true;
181             else
182                 progressbar_block_update = true;
183             wFooter->goToXY(0, Config.statusbar_visibility);
184             *wFooter << message << NC::TermManip::ClearToEOL;
185         }
186 		wFooter->refresh();
187 	}
188 }
189 
mpd()190 void Statusbar::Helpers::mpd()
191 {
192 	Status::update(Mpd.noidle());
193 }
194 
mainHook(const char *)195 bool Statusbar::Helpers::mainHook(const char *)
196 {
197 	Status::trace();
198 	return true;
199 }
200 
promptReturnOneOf(const std::vector<char> & values)201 char Statusbar::Helpers::promptReturnOneOf(const std::vector<char> &values)
202 {
203 	if (values.empty())
204 		throw std::logic_error("empty vector of acceptable input");
205 	NC::Key::Type result;
206 	do
207 	{
208 		wFooter->refresh();
209 		result = wFooter->readKey();
210 		if (result == NC::Key::Ctrl_C || result == NC::Key::Ctrl_G)
211 			throw NC::PromptAborted();
212 	}
213 	while (std::find(values.begin(), values.end(), result) == values.end());
214 	return result;
215 }
216 
operator ()(const char * s) const217 bool Statusbar::Helpers::ImmediatelyReturnOneOf::operator()(const char *s) const
218 {
219 	Status::trace();
220 	return !isOneOf(s);
221 }
222 
operator ()(const char * s)223 bool Statusbar::Helpers::ApplyFilterImmediately::operator()(const char *s)
224 {
225 	using Global::myScreen;
226 	Status::trace();
227 	try {
228 		if (m_w->allowsFiltering() && m_w->currentFilter() != s)
229 		{
230 			m_w->applyFilter(s);
231 			if (myScreen == myPlaylist)
232 				myPlaylist->enableHighlighting();
233 			myScreen->refreshWindow();
234 		}
235 	} catch (boost::bad_expression &) { }
236 	return true;
237 }
238 
operator ()(const char * s)239 bool Statusbar::Helpers::FindImmediately::operator()(const char *s)
240 {
241 	using Global::myScreen;
242 	Status::trace();
243 	try {
244 		if (m_w->allowsSearching() && m_w->searchConstraint() != s)
245 		{
246 			m_w->setSearchConstraint(s);
247 			m_w->search(m_direction, Config.wrapped_search, false);
248 			if (myScreen == myPlaylist)
249 				myPlaylist->enableHighlighting();
250 			myScreen->refreshWindow();
251 		}
252 	} catch (boost::bad_expression &) { }
253 	return true;
254 }
255 
operator ()(const char * s)256 bool Statusbar::Helpers::TryExecuteImmediateCommand::operator()(const char *s)
257 {
258 	bool continue_ = true;
259 	if (m_s != s)
260 	{
261 		m_s = s;
262 		auto cmd = Bindings.findCommand(m_s);
263 		if (cmd && cmd->immediate())
264 			continue_ = false;
265 	}
266 	Status::trace();
267 	return continue_;
268 }
269