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