NeoMutt  2025-09-05-7-geaa2bd
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
question.c
Go to the documentation of this file.
1
30#include "config.h"
31#include <langinfo.h>
32#include <limits.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <string.h>
37#include "mutt/lib.h"
38#include "config/lib.h"
39#include "gui/lib.h"
40#include "color/lib.h"
41#include "key/lib.h"
42#ifdef HAVE_SYS_PARAM_H
43#include <sys/param.h> // IWYU pragma: keep
44#endif
45
62int mw_multi_choice(const char *prompt, const char *letters)
63{
64 struct MuttWindow *win = msgwin_new(true);
65 if (!win)
66 return -1;
67
68 int choice = 0;
69
70 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
71 const struct AttrColor *ac_prompt = merged_color_overlay(ac_normal,
73
75 {
76 const struct AttrColor *ac_opts = merged_color_overlay(ac_prompt,
78 char *cur = NULL;
79
80 while ((cur = strchr(prompt, '(')))
81 {
82 // write the part between prompt and cur using MT_COLOR_PROMPT
83 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
84
85 if (mutt_isalnum(cur[1]) && (cur[2] == ')'))
86 {
87 // we have a single letter within parentheses - MT_COLOR_OPTIONS
88 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
89 prompt = cur + 3;
90 }
91 else
92 {
93 // we have a parenthesis followed by something else
94 msgwin_add_text_n(win, cur, 1, ac_prompt);
95 prompt = cur + 1;
96 }
97 }
98 }
99
100 msgwin_add_text(win, prompt, ac_prompt);
101 msgwin_add_text(win, " ", ac_normal);
102
104 struct MuttWindow *old_focus = window_set_focus(win);
105 window_redraw(win);
106
107 // ---------------------------------------------------------------------------
108 // Event Loop
109 struct KeyEvent event = { 0, OP_NULL };
110 while (true)
111 {
112 event = mutt_getch(GETCH_NO_FLAGS);
113 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
114
115 if (event.op == OP_REPAINT)
116 window_redraw(NULL);
117
118 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
119 continue;
120
121 if ((event.op == OP_ABORT) || key_is_return(event.ch))
122 {
123 choice = -1;
124 break;
125 }
126
127 char *p = strchr(letters, event.ch);
128 if (p)
129 {
130 choice = p - letters + 1;
131 break;
132 }
133
134 if ((event.ch > '0') && (event.ch <= '9'))
135 {
136 choice = event.ch - '0';
137 if (choice <= mutt_str_len(letters))
138 break;
139 }
140 }
141 // ---------------------------------------------------------------------------
142
143 win = msgcont_pop_window();
144 window_set_focus(old_focus);
145 mutt_window_free(&win);
146
147 return choice;
148}
149
173static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
174 struct ConfigDef *cdef, GetChFlags flags)
175{
176 struct MuttWindow *win = msgwin_new(true);
177 if (!win)
178 return MUTT_ABORT;
179
180 char *yes = N_("yes");
181 char *no = N_("no");
182 char *trans_yes = _(yes);
183 char *trans_no = _(no);
184
185 regex_t reyes = { 0 };
186 regex_t reno = { 0 };
187
188 bool reyes_ok = false;
189 bool reno_ok = false;
190
191#ifdef OpenBSD
192 /* OpenBSD only supports locale C and UTF-8
193 * so there is no suitable base system's locale identification
194 * Remove this code immediately if this situation changes! */
195 char rexyes[16] = "^[+1YyYy]";
196 rexyes[6] = mutt_toupper(trans_yes[0]);
197 rexyes[7] = mutt_tolower(trans_yes[0]);
198
199 char rexno[16] = "^[-0NnNn]";
200 rexno[6] = mutt_toupper(trans_no[0]);
201 rexno[7] = mutt_tolower(trans_no[0]);
202
203 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
204 reyes_ok = true;
205
206 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
207 reno_ok = true;
208
209#else
210 char *expr = NULL;
211 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
212 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
213 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
214 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
215#endif
216
217 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
218 {
219 // If all parts of the translation succeeded...
220 yes = trans_yes;
221 no = trans_no;
222 }
223 else
224 {
225 // otherwise, fallback to English
226 if (reyes_ok)
227 {
228 regfree(&reyes);
229 reyes_ok = false;
230 }
231 if (reno_ok)
232 {
233 regfree(&reno);
234 reno_ok = false;
235 }
236 }
237
238 bool show_help_prompt = cdef;
239
240 struct Buffer *text = buf_pool_get();
241 buf_printf(text, "%s ([%s]/%s%s): ", prompt, (def == MUTT_YES) ? yes : no,
242 (def == MUTT_YES) ? no : yes, show_help_prompt ? "/?" : "");
243
246 struct MuttWindow *old_focus = window_set_focus(win);
247
248 struct KeyEvent event = { 0, OP_NULL };
249 window_redraw(NULL);
250 while (true)
251 {
252 event = mutt_getch(flags);
253 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
254 {
255 window_redraw(NULL);
256 mutt_refresh();
257 continue;
258 }
259
260 if (key_is_return(event.ch))
261 break; // Do nothing, use default
262
263 if (event.op == OP_ABORT)
264 {
265 def = MUTT_ABORT;
266 break;
267 }
268
269 char answer[4] = { 0 };
270 answer[0] = event.ch;
271 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'y'))
272 {
273 def = MUTT_YES;
274 break;
275 }
276 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'n'))
277 {
278 def = MUTT_NO;
279 break;
280 }
281 if (show_help_prompt && (event.ch == '?'))
282 {
283 show_help_prompt = false;
285 buf_printf(text, "$%s - %s\n", cdef->name, cdef->docs);
286
287 char hyphen[128] = { 0 };
288 mutt_str_hyphenate(hyphen, sizeof(hyphen), cdef->name);
289 buf_add_printf(text, "https://neomutt.org/guide/reference#%s\n", hyphen);
290
292
293 buf_printf(text, "%s ([%s]/%s): ", prompt, (def == MUTT_YES) ? yes : no,
294 (def == MUTT_YES) ? no : yes);
296 msgwin_add_text(win, NULL, NULL);
297
298 window_redraw(NULL);
299 mutt_refresh();
300 }
301
302 mutt_beep(false);
303 }
304
305 win = msgcont_pop_window();
306 window_set_focus(old_focus);
307 mutt_window_free(&win);
308
309 if (reyes_ok)
310 regfree(&reyes);
311 if (reno_ok)
312 regfree(&reno);
313
314 buf_pool_release(&text);
315 return def;
316}
317
326enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
327{
328 return mw_yesorno(prompt, def, NULL, GETCH_NO_FLAGS);
329}
330
339enum QuadOption query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
340{
341 return mw_yesorno(prompt, def, NULL, GETCH_IGNORE_MACRO);
342}
343
354enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
355 struct ConfigSubset *sub, const char *name)
356{
357 struct HashElem *he = cs_subset_create_inheritance(sub, name);
358 struct HashElem *he_base = cs_get_base(he);
359 ASSERT(CONFIG_TYPE(he_base->type) == DT_BOOL);
360
361 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
362 ASSERT(value != INT_MIN);
363
364 struct ConfigDef *cdef = he_base->data;
365 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
366}
367
378enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
379{
380 struct HashElem *he = cs_subset_create_inheritance(sub, name);
381 struct HashElem *he_base = cs_get_base(he);
382 ASSERT(CONFIG_TYPE(he_base->type) == DT_QUAD);
383
384 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
385 ASSERT(value != INT_MIN);
386
387 if ((value == MUTT_YES) || (value == MUTT_NO))
388 return value;
389
390 struct ConfigDef *cdef = he_base->data;
391 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
392 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
393}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:161
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:204
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:96
Color and attribute parsing.
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:116
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:95
@ MT_COLOR_OPTIONS
Options in prompt.
Definition: color.h:55
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:54
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:57
Convenience wrapper for the config headers.
struct HashElem * cs_get_base(struct HashElem *he)
Find the root Config Item.
Definition: set.c:160
int mutt_toupper(int arg)
Wrapper for toupper(3)
Definition: ctype.c:139
bool mutt_isalnum(int arg)
Wrapper for isalnum(3)
Definition: ctype.c:39
int mutt_tolower(int arg)
Wrapper for tolower(3)
Definition: ctype.c:125
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:79
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:69
struct KeyEvent mutt_getch(GetChFlags flags)
Read a character from the input buffer.
Definition: get.c:210
static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def, struct ConfigDef *cdef, GetChFlags flags)
Ask the user a Yes/No question offering help -.
Definition: question.c:173
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:62
#define mutt_debug(LEVEL,...)
Definition: logging2.h:90
Convenience wrapper for the gui headers.
Manage keymappings.
uint8_t GetChFlags
Flags for mutt_getch(), e.g. GETCH_NO_FLAGS.
Definition: lib.h:51
#define GETCH_IGNORE_MACRO
Don't use MacroEvents.
Definition: lib.h:53
#define GETCH_NO_FLAGS
No flags are set.
Definition: lib.h:52
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:44
const struct AttrColor * merged_color_overlay(const struct AttrColor *base, const struct AttrColor *over)
Combine two colours.
Definition: merged.c:107
void msgcont_push_window(struct MuttWindow *win)
Add a window to the Container Stack.
Definition: msgcont.c:93
struct MuttWindow * msgcont_pop_window(void)
Remove the last Window from the Container Stack.
Definition: msgcont.c:57
struct MuttWindow * msgwin_new(bool interactive)
Create the Message Window.
Definition: msgwin.c:371
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:519
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition: msgwin.c:419
void msgwin_add_text_n(struct MuttWindow *win, const char *text, int bytes, const struct AttrColor *ac_color)
Add some text to the Message Window.
Definition: msgwin.c:450
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition: msgwin.c:484
Convenience wrapper for the library headers.
#define N_(a)
Definition: message.h:32
#define _(a)
Definition: message.h:28
void mutt_str_hyphenate(char *buf, size_t buflen, const char *str)
Hyphenate a snake-case string.
Definition: string.c:848
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:497
#define key_is_return(ch)
Definition: mutt_curses.h:57
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
Definition: mutt_window.c:599
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:205
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:649
#define OP_TIMEOUT
1 second with no events
Definition: opcodes.h:36
#define OP_REPAINT
Repaint is needed.
Definition: opcodes.h:34
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition: opcodes.h:37
struct Buffer * buf_pool_get(void)
Get a Buffer from the pool.
Definition: pool.c:82
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:96
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 query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question ignoring the macro buffer.
Definition: question.c:339
enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def, struct ConfigSubset *sub, const char *name)
Ask the user a Yes/No question offering help.
Definition: question.c:354
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:378
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:326
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:50
#define ASSERT(COND)
Definition: signal2.h:60
A curses colour and its attributes.
Definition: attr.h:66
String manipulation buffer.
Definition: buffer.h:36
Definition: set.h:62
const char * name
User-visible name.
Definition: set.h:63
const char * docs
One-liner description.
Definition: set.h:81
A set of inherited config items.
Definition: subset.h:46
The item stored in a Hash Table.
Definition: hash.h:44
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:45
void * data
User-supplied data.
Definition: hash.h:47
An event such as a keypress.
Definition: lib.h:81
int op
Function opcode, e.g. OP_HELP.
Definition: lib.h:83
int ch
Raw key pressed.
Definition: lib.h:82
intptr_t cs_subset_he_native_get(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *err)
Natively get the value of a HashElem config item.
Definition: subset.c:260
struct HashElem * cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
Create a Subset config item (inherited)
Definition: subset.c:210
#define CONFIG_TYPE(t)
Definition: types.h:49
@ DT_BOOL
boolean option
Definition: types.h:32
@ DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:40