1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2020 The Music Player Daemon Project
3  * Project homepage: http://musicpd.org
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 along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 #include "screen_utils.hxx"
21 #include "screen.hxx"
22 #include "config.h"
23 #include "i18n.h"
24 #include "Options.hxx"
25 #include "Styles.hxx"
26 #include "wreadln.hxx"
27 #include "ncmpc.hxx"
28 #include "config.h"
29 
30 #ifndef _WIN32
31 #include "WaitUserInput.hxx"
32 #endif
33 
34 #include <errno.h>
35 #include <string.h>
36 
37 void
screen_bell()38 screen_bell() noexcept
39 {
40 	if (options.audible_bell)
41 		beep();
42 	if (options.visible_bell)
43 		flash();
44 }
45 
46 static constexpr bool
ignore_key(int key)47 ignore_key(int key) noexcept
48 {
49 	return
50 #ifdef HAVE_GETMOUSE
51 		/* ignore mouse events */
52 		key == KEY_MOUSE ||
53 #endif
54 		key == ERR;
55 }
56 
57 int
screen_getch(const char * prompt)58 screen_getch(const char *prompt) noexcept
59 {
60 	WINDOW *w = screen->status_bar.GetWindow().w;
61 
62 	SelectStyle(w, Style::STATUS_ALERT);
63 	werase(w);
64 	wmove(w, 0, 0);
65 	waddstr(w, prompt);
66 
67 	echo();
68 	curs_set(1);
69 
70 #ifndef _WIN32
71 	WaitUserInput wui;
72 #endif
73 
74 	int key;
75 	do {
76 		key = wgetch(w);
77 
78 #ifndef _WIN32
79 		if (key == ERR && errno == EAGAIN) {
80 			if (wui.Wait())
81 				continue;
82 			else
83 				break;
84 		}
85 #endif
86 	} while (ignore_key(key));
87 
88 	noecho();
89 	curs_set(0);
90 
91 	return key;
92 }
93 
94 bool
screen_get_yesno(const char * _prompt,bool def)95 screen_get_yesno(const char *_prompt, bool def) noexcept
96 {
97 	/* NOTE: if one day a translator decides to use a multi-byte character
98 	   for one of the yes/no keys, we'll have to parse it properly */
99 
100 	char prompt[256];
101 	snprintf(prompt, sizeof(prompt),
102 		 "%s [%s/%s] ", _prompt,
103 		 YES_TRANSLATION, NO_TRANSLATION);
104 	int key = tolower(screen_getch(prompt));
105 	if (key == YES_TRANSLATION[0])
106 		return true;
107 	else if (key == NO_TRANSLATION[0])
108 		return false;
109 	else
110 		return def;
111 }
112 
113 std::string
screen_readln(const char * prompt,const char * value,History * history,Completion * completion)114 screen_readln(const char *prompt,
115 	      const char *value,
116 	      History *history,
117 	      Completion *completion) noexcept
118 {
119 	auto *window = &screen->status_bar.GetWindow();
120 	WINDOW *w = window->w;
121 
122 	wmove(w, 0,0);
123 	curs_set(1);
124 
125 	if (prompt != nullptr) {
126 		SelectStyle(w, Style::STATUS_ALERT);
127 		waddstr(w, prompt);
128 		waddstr(w, ": ");
129 	}
130 
131 	SelectStyle(w, Style::STATUS);
132 	wattron(w, A_REVERSE);
133 
134 	auto result = wreadln(w, value, window->size.width,
135 			      history, completion);
136 	curs_set(0);
137 	return result;
138 }
139 
140 std::string
screen_read_password(const char * prompt)141 screen_read_password(const char *prompt) noexcept
142 {
143 	auto *window = &screen->status_bar.GetWindow();
144 	WINDOW *w = window->w;
145 
146 	wmove(w, 0,0);
147 	curs_set(1);
148 	SelectStyle(w, Style::STATUS_ALERT);
149 
150 	if (prompt == nullptr)
151 		prompt = _("Password");
152 
153 	waddstr(w, prompt);
154 	waddstr(w, ": ");
155 
156 	SelectStyle(w, Style::STATUS);
157 	wattron(w, A_REVERSE);
158 
159 	auto result = wreadln_masked(w, nullptr, window->size.width);
160 	curs_set(0);
161 	return result;
162 }
163 
164 static const char *
CompletionDisplayString(const char * value)165 CompletionDisplayString(const char *value) noexcept
166 {
167 	const char *slash = strrchr(value, '/');
168 	if (slash == nullptr)
169 		return value;
170 
171 	if (slash[1] == 0) {
172 		/* if the string ends with a slash (directory URIs
173 		   usually do), backtrack to the preceding slash (if
174 		   any) */
175 		while (slash > value) {
176 			--slash;
177 
178 			if (*slash == '/')
179 				return slash + 1;
180 		}
181 	}
182 
183 	return slash;
184 }
185 
186 void
screen_display_completion_list(Completion::Range range)187 screen_display_completion_list(Completion::Range range) noexcept
188 {
189 	static Completion::Range prev_range;
190 	static size_t prev_length = 0;
191 	static unsigned offset = 0;
192 	WINDOW *w = screen->main_window.w;
193 
194 	size_t length = std::distance(range.begin(), range.end());
195 	if (range == prev_range && length == prev_length) {
196 		offset += screen->main_window.size.height;
197 		if (offset >= length)
198 			offset = 0;
199 	} else {
200 		prev_range = range;
201 		prev_length = length;
202 		offset = 0;
203 	}
204 
205 	SelectStyle(w, Style::STATUS_ALERT);
206 
207 	auto i = std::next(range.begin(), offset);
208 	for (unsigned y = 0; y < screen->main_window.size.height; ++y, ++i) {
209 		wmove(w, y, 0);
210 		if (i == range.end())
211 			break;
212 
213 		const char *value = i->c_str();
214 		waddstr(w, CompletionDisplayString(value));
215 		wclrtoeol(w);
216 	}
217 
218 	wclrtobot(w);
219 
220 	wrefresh(w);
221 	SelectStyle(w, Style::LIST);
222 }
223