7a9104f9e381a79516ba8b7efc573e30fde1a9e3
[ncmpc-debian.git] / src / screen_utils.cxx
1 /* ncmpc (Ncurses MPD Client)
2  * (c) 2004-2018 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 "mpdclient.hxx"
23 #include "config.h"
24 #include "i18n.h"
25 #include "options.hxx"
26 #include "Styles.hxx"
27 #include "wreadln.hxx"
28 #include "ncmpc.hxx"
29
30 #include <mpd/client.h>
31
32 #include <string.h>
33
34 void
35 screen_bell()
36 {
37         if (options.audible_bell)
38                 beep();
39         if (options.visible_bell)
40                 flash();
41 }
42
43 static bool
44 ignore_key(int key)
45 {
46         return
47 #ifdef HAVE_GETMOUSE
48                 /* ignore mouse events */
49                 key == KEY_MOUSE ||
50 #endif
51                 key == ERR;
52 }
53
54 int
55 screen_getch(const char *prompt)
56 {
57         WINDOW *w = screen->status_bar.GetWindow().w;
58
59         SelectStyle(w, Style::STATUS_ALERT);
60         werase(w);
61         wmove(w, 0, 0);
62         waddstr(w, prompt);
63
64         echo();
65         curs_set(1);
66
67         int key;
68         while (ignore_key(key = wgetch(w))) {}
69
70         noecho();
71         curs_set(0);
72
73         return key;
74 }
75
76 bool
77 screen_get_yesno(const char *_prompt, bool def)
78 {
79         /* NOTE: if one day a translator decides to use a multi-byte character
80            for one of the yes/no keys, we'll have to parse it properly */
81
82         char prompt[256];
83         snprintf(prompt, sizeof(prompt),
84                  "%s [%s/%s] ", _prompt,
85                  YES_TRANSLATION, NO_TRANSLATION);
86         int key = tolower(screen_getch(prompt));
87         if (key == YES_TRANSLATION[0])
88                 return true;
89         else if (key == NO_TRANSLATION[0])
90                 return false;
91         else
92                 return def;
93 }
94
95 std::string
96 screen_readln(const char *prompt,
97               const char *value,
98               History *history,
99               Completion *completion)
100 {
101         auto *window = &screen->status_bar.GetWindow();
102         WINDOW *w = window->w;
103
104         wmove(w, 0,0);
105         curs_set(1);
106         SelectStyle(w, Style::STATUS_ALERT);
107         auto result = wreadln(w, prompt, value, window->size.width,
108                               history, completion);
109         curs_set(0);
110         return result;
111 }
112
113 std::string
114 screen_read_password(const char *prompt)
115 {
116         auto *window = &screen->status_bar.GetWindow();
117         WINDOW *w = window->w;
118
119         wmove(w, 0,0);
120         curs_set(1);
121         SelectStyle(w, Style::STATUS_ALERT);
122
123         if (prompt == nullptr)
124                 prompt = _("Password");
125
126         auto result = wreadln_masked(w, prompt, nullptr, window->size.width);
127         curs_set(0);
128         return result;
129 }
130
131 static const char *
132 CompletionDisplayString(const char *value)
133 {
134         const char *slash = strrchr(value, '/');
135         if (slash == nullptr)
136                 return value;
137
138         if (slash[1] == 0) {
139                 /* if the string ends with a slash (directory URIs
140                    usually do), backtrack to the preceding slash (if
141                    any) */
142                 while (slash > value) {
143                         --slash;
144
145                         if (*slash == '/')
146                                 return slash + 1;
147                 }
148         }
149
150         return slash;
151 }
152
153 void
154 screen_display_completion_list(Completion::Range range)
155 {
156         static Completion::Range prev_range;
157         static size_t prev_length = 0;
158         static unsigned offset = 0;
159         WINDOW *w = screen->main_window.w;
160
161         size_t length = std::distance(range.begin(), range.end());
162         if (range == prev_range && length == prev_length) {
163                 offset += screen->main_window.size.height;
164                 if (offset >= length)
165                         offset = 0;
166         } else {
167                 prev_range = range;
168                 prev_length = length;
169                 offset = 0;
170         }
171
172         SelectStyle(w, Style::STATUS_ALERT);
173
174         auto i = std::next(range.begin(), offset);
175         for (unsigned y = 0; y < screen->main_window.size.height; ++y, ++i) {
176                 wmove(w, y, 0);
177                 if (i == range.end())
178                         break;
179
180                 const char *value = i->c_str();
181                 waddstr(w, CompletionDisplayString(value));
182                 wclrtoeol(w);
183         }
184
185         wclrtobot(w);
186
187         wrefresh(w);
188         SelectStyle(w, Style::LIST);
189 }