NeoMutt  2023-03-22-27-g3cb248
Teaching an old dog new tricks
DOXYGEN
question.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <ctype.h>
31#include <langinfo.h>
32#include <stdbool.h>
33#include <stdio.h>
34#include <string.h>
35#include "mutt/lib.h"
36#include "config/lib.h"
37#include "gui/lib.h"
38#include "color/lib.h"
39#include "globals.h"
40#include "keymap.h"
41#include "mutt_logging.h"
42#include "opcodes.h"
43#ifdef HAVE_SYS_PARAM_H
44#include <sys/param.h>
45#endif
46
54int mutt_multi_choice(const char *prompt, const char *letters)
55{
56 struct MuttWindow *win = msgwin_get_window();
57 if (!win)
58 return -1;
59
60 struct KeyEvent ch = { OP_NULL, OP_NULL };
61 int choice;
62 bool redraw = true;
63 int prompt_lines = 1;
64
65 struct AttrColor *ac_opts = NULL;
67 {
70
72 ac_opts = merged_color_overlay(ac_base, ac_opts);
73 }
74
75 struct MuttWindow *old_focus = window_set_focus(win);
77 window_redraw(NULL);
78 while (true)
79 {
80 if (redraw || SigWinch)
81 {
82 redraw = false;
83 if (SigWinch)
84 {
85 SigWinch = false;
87 clearok(stdscr, true);
88 window_redraw(NULL);
89 }
90 if (win->state.cols)
91 {
92 int width = mutt_strwidth(prompt) + 2; // + '?' + space
93 /* If we're going to colour the options,
94 * make an assumption about the modified prompt size. */
95 if (ac_opts)
96 width -= 2 * mutt_str_len(letters);
97
98 prompt_lines = (width + win->state.cols - 1) / win->state.cols;
99 prompt_lines = MAX(1, MIN(3, prompt_lines));
100 }
101 if (prompt_lines != win->state.rows)
102 {
103 msgwin_set_height(prompt_lines);
104 window_redraw(NULL);
105 }
106
107 mutt_window_move(win, 0, 0);
108
109 if (ac_opts)
110 {
111 char *cur = NULL;
112
113 while ((cur = strchr(prompt, '(')))
114 {
115 // write the part between prompt and cur using MT_COLOR_PROMPT
117 mutt_window_addnstr(win, prompt, cur - prompt);
118
119 if (isalnum(cur[1]) && (cur[2] == ')'))
120 {
121 // we have a single letter within parentheses
122 mutt_curses_set_color(ac_opts);
123 mutt_window_addch(win, cur[1]);
124 prompt = cur + 3;
125 }
126 else
127 {
128 // we have a parenthesis followed by something else
129 mutt_window_addch(win, cur[0]);
130 prompt = cur + 1;
131 }
132 }
133 }
134
136 mutt_window_addstr(win, prompt);
138
139 mutt_window_addch(win, ' ');
141 }
142
143 mutt_refresh();
144 /* SigWinch is not processed unless timeout is set */
145 ch = mutt_getch_timeout(30 * 1000);
146 if (ch.op == OP_TIMEOUT)
147 continue;
148 if (ch.op == OP_ABORT || CI_is_return(ch.ch))
149 {
150 choice = -1;
151 break;
152 }
153 else
154 {
155 char *p = strchr(letters, ch.ch);
156 if (p)
157 {
158 choice = p - letters + 1;
159 break;
160 }
161 else if ((ch.ch <= '9') && (ch.ch > '0'))
162 {
163 choice = ch.ch - '0';
164 if (choice <= mutt_str_len(letters))
165 break;
166 }
167 }
168 mutt_beep(false);
169 }
170
171 if (win->state.rows == 1)
172 {
173 mutt_window_clearline(win, 0);
174 }
175 else
176 {
178 window_redraw(NULL);
179 }
180
182 window_set_focus(old_focus);
184 mutt_refresh();
185 return choice;
186}
187
194enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
195{
196 struct MuttWindow *win = msgwin_get_window();
197 if (!win)
198 return MUTT_ABORT;
199
200 struct KeyEvent ch = { OP_NULL, OP_NULL };
201 char *answer_string = NULL;
202 int answer_string_wid, msg_wid;
203 size_t trunc_msg_len;
204 bool redraw = true;
205 int prompt_lines = 1;
206 char answer[2] = { 0 };
207
208 char *yes = N_("yes");
209 char *no = N_("no");
210 char *trans_yes = _(yes);
211 char *trans_no = _(no);
212
213 regex_t reyes = { 0 };
214 regex_t reno = { 0 };
215
216 bool reyes_ok = false;
217 bool reno_ok = false;
218
219#ifdef OpenBSD
220 /* OpenBSD only supports locale C and UTF-8
221 * so there is no suitable base system's locale identification
222 * Remove this code immediately if this situation changes! */
223 char rexyes[16] = "^[+1YyYy]";
224 rexyes[6] = toupper(trans_yes[0]);
225 rexyes[7] = tolower(trans_yes[0]);
226
227 char rexno[16] = "^[-0NnNn]";
228 rexno[6] = toupper(trans_no[0]);
229 rexno[7] = tolower(trans_no[0]);
230
231 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
232 reyes_ok = true;
233
234 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
235 reno_ok = true;
236
237#else
238 char *expr = NULL;
239 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
240 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
241 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
242 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
243#endif
244
245 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
246 {
247 // If all parts of the translation succeeded...
248 yes = trans_yes;
249 no = trans_no;
250 }
251 else
252 {
253 // otherwise, fallback to English
254 if (reyes_ok)
255 {
256 regfree(&reyes);
257 reyes_ok = false;
258 }
259 if (reno_ok)
260 {
261 regfree(&reno);
262 reno_ok = false;
263 }
264 }
265
266 /* In order to prevent the default answer to the question to wrapped
267 * around the screen in the event the question is wider than the screen,
268 * ensure there is enough room for the answer and truncate the question
269 * to fit. */
270 mutt_str_asprintf(&answer_string, " ([%s]/%s): ", (def == MUTT_YES) ? yes : no,
271 (def == MUTT_YES) ? no : yes);
272 answer_string_wid = mutt_strwidth(answer_string);
273 msg_wid = mutt_strwidth(msg);
274
275 struct MuttWindow *old_focus = window_set_focus(win);
276
278 window_redraw(NULL);
279 while (true)
280 {
281 if (redraw || SigWinch)
282 {
283 redraw = false;
284 if (SigWinch)
285 {
286 SigWinch = false;
288 clearok(stdscr, true);
289 window_redraw(NULL);
290 }
291 if (win->state.cols)
292 {
293 prompt_lines = (msg_wid + answer_string_wid + win->state.cols - 1) /
294 win->state.cols;
295 prompt_lines = MAX(1, MIN(3, prompt_lines));
296 }
297 if (prompt_lines != win->state.rows)
298 {
299 msgwin_set_height(prompt_lines);
300 window_redraw(NULL);
301 }
302
303 /* maxlen here is sort of arbitrary, so pick a reasonable upper bound */
304 trunc_msg_len = mutt_wstr_trunc(msg,
305 (size_t) 4 * prompt_lines * win->state.cols,
306 ((size_t) prompt_lines * win->state.cols) - answer_string_wid,
307 NULL);
308
309 mutt_window_move(win, 0, 0);
311 mutt_window_addnstr(win, msg, trunc_msg_len);
312 mutt_window_addstr(win, answer_string);
315 }
316
317 mutt_refresh();
318 /* SigWinch is not processed unless timeout is set */
319 ch = mutt_getch_timeout(30 * 1000);
320 if (ch.op == OP_TIMEOUT)
321 continue;
322 if (CI_is_return(ch.ch))
323 break;
324 if (ch.op == OP_ABORT)
325 {
326 def = MUTT_ABORT;
327 break;
328 }
329
330 answer[0] = ch.ch;
331 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'y'))
332 {
333 def = MUTT_YES;
334 break;
335 }
336 else if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(ch.ch) == 'n'))
337 {
338 def = MUTT_NO;
339 break;
340 }
341 else
342 {
343 mutt_beep(false);
344 }
345 }
346 window_set_focus(old_focus);
348
349 FREE(&answer_string);
350
351 if (reyes_ok)
352 regfree(&reyes);
353 if (reno_ok)
354 regfree(&reno);
355
356 if (win->state.rows == 1)
357 {
358 mutt_window_clearline(win, 0);
359 }
360 else
361 {
363 window_redraw(NULL);
364 }
365
366 if (def == MUTT_ABORT)
367 {
368 /* when the users cancels with ^G, clear the message stored with
369 * mutt_message() so it isn't displayed when the screen is refreshed. */
371 }
372 else
373 {
374 mutt_window_addstr(win, (char *) ((def == MUTT_YES) ? yes : no));
375 mutt_refresh();
376 }
377 return def;
378}
379
386enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
387{
388 switch (opt)
389 {
390 case MUTT_YES:
391 case MUTT_NO:
392 return opt;
393
394 default:
395 opt = mutt_yesorno(prompt, (opt == MUTT_ASKYES) ? MUTT_YES : MUTT_NO);
397 return opt;
398 }
399
400 /* not reached */
401}
Color and attribute parsing.
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:95
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:74
@ MT_COLOR_OPTIONS
Options in prompt.
Definition: color.h:58
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:57
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:60
Convenience wrapper for the config headers.
struct KeyEvent mutt_getch_timeout(int delay)
Get an event with a timeout.
Definition: curs_lib.c:196
size_t mutt_wstr_trunc(const char *src, size_t maxlen, size_t maxwid, size_t *width)
Work out how to truncate a widechar string.
Definition: curs_lib.c:859
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:139
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:129
size_t mutt_strwidth(const char *s)
Measure a string's width in screen cells.
Definition: curs_lib.c:908
SIG_ATOMIC_VOLATILE_T SigWinch
true after SIGWINCH is received
Definition: globals.c:59
Convenience wrapper for the gui headers.
Manage keymappings.
#define FREE(x)
Definition: memory.h:43
#define MIN(a, b)
Definition: memory.h:31
#define MAX(a, b)
Definition: memory.h:30
struct AttrColor * merged_color_overlay(struct AttrColor *base, struct AttrColor *over)
Combine two colours.
Definition: merged.c:109
void msgwin_set_height(short height)
Resize the Message Window.
Definition: msgwin.c:283
struct MuttWindow * msgwin_get_window(void)
Get the Message Window pointer.
Definition: msgwin.c:260
void msgwin_clear_text(void)
Clear the text in the Message Window.
Definition: msgwin.c:249
Convenience wrapper for the library headers.
#define N_(a)
Definition: message.h:32
#define _(a)
Definition: message.h:28
int mutt_str_asprintf(char **strp, const char *fmt,...)
Definition: string.c:1031
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:567
enum MuttCursorState mutt_curses_set_cursor(enum MuttCursorState state)
Set the cursor state.
Definition: mutt_curses.c:96
void mutt_curses_set_color(struct AttrColor *ac)
Set the colour and attributes for text.
Definition: mutt_curses.c:40
struct AttrColor * mutt_curses_set_normal_backed_color_by_id(enum ColorId cid)
Set the colour and attributes by the colour id.
Definition: mutt_curses.c:65
struct AttrColor * mutt_curses_set_color_by_id(enum ColorId cid)
Set the colour and attributes by the colour id.
Definition: mutt_curses.c:81
void mutt_resize_screen(void)
Update NeoMutt's opinion about the window size (CURSES)
Definition: resize.c:72
MuttCursorState
Cursor states for mutt_curses_set_cursor()
Definition: mutt_curses.h:52
@ MUTT_CURSOR_VISIBLE
Display a normal cursor.
Definition: mutt_curses.h:54
#define CI_is_return(ch)
Definition: mutt_curses.h:45
void mutt_clear_error(void)
Clear the message line (bottom line of screen)
Definition: mutt_logging.c:73
NeoMutt Logging.
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:605
int mutt_window_move(struct MuttWindow *win, int col, int row)
Move the cursor in a Window.
Definition: mutt_window.c:294
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:660
void mutt_window_clearline(struct MuttWindow *win, int row)
Clear a row of a Window.
Definition: mutt_window.c:229
int mutt_window_addstr(struct MuttWindow *win, const char *str)
Write a string to a Window.
Definition: mutt_window.c:410
int mutt_window_addnstr(struct MuttWindow *win, const char *str, int num)
Write a partial string to a Window.
Definition: mutt_window.c:395
void mutt_window_clrtoeol(struct MuttWindow *win)
Clear to the end of the line.
Definition: mutt_window.c:241
int mutt_window_addch(struct MuttWindow *win, int ch)
Write one character to a Window.
Definition: mutt_window.c:382
All user-callable functions.
#define OP_TIMEOUT
Definition: opcodes.h:32
#define OP_ABORT
Definition: opcodes.h:33
QuadOption
Possible values for a quad-option.
Definition: quad.h:36
@ MUTT_ABORT
User aborted the question (with Ctrl-G)
Definition: quad.h:37
@ MUTT_NO
User answered 'No', or assume 'No'.
Definition: quad.h:38
@ MUTT_ASKYES
Ask the user, defaulting to 'Yes'.
Definition: quad.h:41
@ MUTT_YES
User answered 'Yes', or assume 'Yes'.
Definition: quad.h:39
enum QuadOption mutt_yesorno(const char *msg, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:194
int mutt_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question.
Definition: question.c:54
enum QuadOption query_quadoption(enum QuadOption opt, const char *prompt)
Ask the user a quad-question.
Definition: question.c:386
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:53
A curses colour and its attributes.
Definition: attr.h:35
An event such as a keypress.
Definition: keymap.h:65
int op
Function opcode, e.g. OP_HELP.
Definition: keymap.h:67
int ch
Raw key pressed.
Definition: keymap.h:66
struct WindowState state
Current state of the Window.
Definition: mutt_window.h:127
short cols
Number of columns, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:60
short rows
Number of rows, can be MUTT_WIN_SIZE_UNLIMITED.
Definition: mutt_window.h:61