NeoMutt  2025-09-05-55-g97fc89
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
question.c
Go to the documentation of this file.
1
23
29
30#include "config.h"
31#include <langinfo.h>
32#include <limits.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <string.h>
36#include "mutt/lib.h"
37#include "config/lib.h"
38#include "gui/lib.h"
39#include "color/lib.h"
40#include "key/lib.h"
41#ifdef HAVE_SYS_PARAM_H
42#include <sys/param.h> // IWYU pragma: keep
43#endif
44
61int mw_multi_choice(const char *prompt, const char *letters)
62{
63 struct MuttWindow *win = msgwin_new(true);
64 if (!win)
65 return -1;
66
67 int choice = 0;
68
69 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
70 const struct AttrColor *ac_prompt = merged_color_overlay(ac_normal,
72
74 {
75 const struct AttrColor *ac_opts = merged_color_overlay(ac_prompt,
77 char *cur = NULL;
78
79 while ((cur = strchr(prompt, '(')))
80 {
81 // write the part between prompt and cur using MT_COLOR_PROMPT
82 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
83
84 if (mutt_isalnum(cur[1]) && (cur[2] == ')'))
85 {
86 // we have a single letter within parentheses - MT_COLOR_OPTIONS
87 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
88 prompt = cur + 3;
89 }
90 else
91 {
92 // we have a parenthesis followed by something else
93 msgwin_add_text_n(win, cur, 1, ac_prompt);
94 prompt = cur + 1;
95 }
96 }
97 }
98
99 msgwin_add_text(win, prompt, ac_prompt);
100 msgwin_add_text(win, " ", ac_normal);
101
103 struct MuttWindow *old_focus = window_set_focus(win);
104 window_redraw(win);
105
106 // ---------------------------------------------------------------------------
107 // Event Loop
108 struct KeyEvent event = { 0, OP_NULL };
109 while (true)
110 {
111 event = mutt_getch(GETCH_NO_FLAGS);
112 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
113
114 if (event.op == OP_REPAINT)
115 window_redraw(NULL);
116
117 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
118 continue;
119
120 if ((event.op == OP_ABORT) || key_is_return(event.ch))
121 {
122 choice = -1;
123 break;
124 }
125
126 char *p = strchr(letters, event.ch);
127 if (p)
128 {
129 choice = p - letters + 1;
130 break;
131 }
132
133 if ((event.ch > '0') && (event.ch <= '9'))
134 {
135 choice = event.ch - '0';
136 if (choice <= mutt_str_len(letters))
137 break;
138 }
139 }
140 // ---------------------------------------------------------------------------
141
142 win = msgcont_pop_window();
143 window_set_focus(old_focus);
144 mutt_window_free(&win);
145
146 return choice;
147}
148
172static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
173 struct ConfigDef *cdef, GetChFlags flags)
174{
175 struct MuttWindow *win = msgwin_new(true);
176 if (!win)
177 return MUTT_ABORT;
178
179 char *yes = N_("yes");
180 char *no = N_("no");
181 char *trans_yes = _(yes);
182 char *trans_no = _(no);
183
184 regex_t reyes = { 0 };
185 regex_t reno = { 0 };
186
187 bool reyes_ok = false;
188 bool reno_ok = false;
189
190#ifdef OpenBSD
191 /* OpenBSD only supports locale C and UTF-8
192 * so there is no suitable base system's locale identification
193 * Remove this code immediately if this situation changes! */
194 char rexyes[16] = "^[+1YyYy]";
195 rexyes[6] = mutt_toupper(trans_yes[0]);
196 rexyes[7] = mutt_tolower(trans_yes[0]);
197
198 char rexno[16] = "^[-0NnNn]";
199 rexno[6] = mutt_toupper(trans_no[0]);
200 rexno[7] = mutt_tolower(trans_no[0]);
201
202 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
203 reyes_ok = true;
204
205 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
206 reno_ok = true;
207
208#else
209 char *expr = NULL;
210 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
211 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
212 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
213 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
214#endif
215
216 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
217 {
218 // If all parts of the translation succeeded...
219 yes = trans_yes;
220 no = trans_no;
221 }
222 else
223 {
224 // otherwise, fallback to English
225 if (reyes_ok)
226 {
227 regfree(&reyes);
228 reyes_ok = false;
229 }
230 if (reno_ok)
231 {
232 regfree(&reno);
233 reno_ok = false;
234 }
235 }
236
237 bool show_help_prompt = cdef;
238
239 struct Buffer *text = buf_pool_get();
240 buf_printf(text, "%s ([%s]/%s%s): ", prompt, (def == MUTT_YES) ? yes : no,
241 (def == MUTT_YES) ? no : yes, show_help_prompt ? "/?" : "");
242
245 struct MuttWindow *old_focus = window_set_focus(win);
246
247 struct KeyEvent event = { 0, OP_NULL };
248 window_redraw(NULL);
249 while (true)
250 {
251 event = mutt_getch(flags);
252 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
253 {
254 window_redraw(NULL);
255 mutt_refresh();
256 continue;
257 }
258
259 if (key_is_return(event.ch))
260 break; // Do nothing, use default
261
262 if (event.op == OP_ABORT)
263 {
264 def = MUTT_ABORT;
265 break;
266 }
267
268 char answer[4] = { 0 };
269 answer[0] = event.ch;
270 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'y'))
271 {
272 def = MUTT_YES;
273 break;
274 }
275 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (mutt_tolower(event.ch) == 'n'))
276 {
277 def = MUTT_NO;
278 break;
279 }
280 if (show_help_prompt && (event.ch == '?'))
281 {
282 show_help_prompt = false;
284 buf_printf(text, "$%s - %s\n", cdef->name, cdef->docs);
285
286 char hyphen[128] = { 0 };
287 mutt_str_hyphenate(hyphen, sizeof(hyphen), cdef->name);
288 buf_add_printf(text, "https://neomutt.org/guide/reference#%s\n", hyphen);
289
291
292 buf_printf(text, "%s ([%s]/%s): ", prompt, (def == MUTT_YES) ? yes : no,
293 (def == MUTT_YES) ? no : yes);
295 msgwin_add_text(win, NULL, NULL);
296
297 window_redraw(NULL);
298 mutt_refresh();
299 }
300
301 mutt_beep(false);
302 }
303
304 win = msgcont_pop_window();
305 window_set_focus(old_focus);
306 mutt_window_free(&win);
307
308 if (reyes_ok)
309 regfree(&reyes);
310 if (reno_ok)
311 regfree(&reno);
312
313 buf_pool_release(&text);
314 return def;
315}
316
325enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
326{
327 return mw_yesorno(prompt, def, NULL, GETCH_NO_FLAGS);
328}
329
338enum QuadOption query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
339{
340 return mw_yesorno(prompt, def, NULL, GETCH_IGNORE_MACRO);
341}
342
353enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
354 struct ConfigSubset *sub, const char *name)
355{
356 struct HashElem *he = cs_subset_create_inheritance(sub, name);
357 struct HashElem *he_base = cs_get_base(he);
358 ASSERT(CONFIG_TYPE(he_base->type) == DT_BOOL);
359
360 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
361 ASSERT(value != INT_MIN);
362
363 struct ConfigDef *cdef = he_base->data;
364 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
365}
366
377enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
378{
379 struct HashElem *he = cs_subset_create_inheritance(sub, name);
380 struct HashElem *he_base = cs_get_base(he);
381 ASSERT(CONFIG_TYPE(he_base->type) == DT_QUAD);
382
383 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
384 ASSERT(value != INT_MIN);
385
386 if ((value == MUTT_YES) || (value == MUTT_NO))
387 return value;
388
389 struct ConfigDef *cdef = he_base->data;
390 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
391 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
392}
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:172
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition question.c:61
#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:52
#define GETCH_IGNORE_MACRO
Don't use MacroEvents.
Definition lib.h:54
#define GETCH_NO_FLAGS
No flags are set.
Definition lib.h:53
@ 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:370
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition msgwin.c:518
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition msgwin.c:418
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:449
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition msgwin.c:483
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:849
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition string.c:498
#define key_is_return(ch)
Definition mutt_curses.h:57
void window_redraw(struct MuttWindow *win)
Reflow, recalc and repaint a tree of Windows.
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
#define OP_TIMEOUT
1 second with no events
Definition opcodes.h:35
#define OP_REPAINT
Repaint is needed.
Definition opcodes.h:34
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition opcodes.h:36
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:338
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:353
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition question.c:377
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition question.c:325
#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
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:82
int op
Function opcode, e.g. OP_HELP.
Definition lib.h:84
int ch
Raw key pressed.
Definition lib.h:83
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:264
struct HashElem * cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
Create a Subset config item (inherited)
Definition subset.c:214
#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