NeoMutt  2024-12-12-14-g7b49f7
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 <ctype.h>
32#include <langinfo.h>
33#include <limits.h>
34#include <stdbool.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <string.h>
38#include "mutt/lib.h"
39#include "config/lib.h"
40#include "gui/lib.h"
41#include "color/lib.h"
42#include "key/lib.h"
43#ifdef HAVE_SYS_PARAM_H
44#include <sys/param.h> // IWYU pragma: keep
45#endif
46
63int mw_multi_choice(const char *prompt, const char *letters)
64{
65 struct MuttWindow *win = msgwin_new(true);
66 if (!win)
67 return -1;
68
69 int choice = 0;
70
71 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
72 const struct AttrColor *ac_prompt = merged_color_overlay(ac_normal,
74
76 {
77 const struct AttrColor *ac_opts = merged_color_overlay(ac_prompt,
79 char *cur = NULL;
80
81 while ((cur = strchr(prompt, '(')))
82 {
83 // write the part between prompt and cur using MT_COLOR_PROMPT
84 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
85
86 if (isalnum(cur[1]) && (cur[2] == ')'))
87 {
88 // we have a single letter within parentheses - MT_COLOR_OPTIONS
89 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
90 prompt = cur + 3;
91 }
92 else
93 {
94 // we have a parenthesis followed by something else
95 msgwin_add_text_n(win, cur, 1, ac_prompt);
96 prompt = cur + 1;
97 }
98 }
99 }
100
101 msgwin_add_text(win, prompt, ac_prompt);
102 msgwin_add_text(win, " ", ac_normal);
103
105 struct MuttWindow *old_focus = window_set_focus(win);
106 window_redraw(win);
107
108 // ---------------------------------------------------------------------------
109 // Event Loop
110 struct KeyEvent event = { 0, OP_NULL };
111 while (true)
112 {
113 event = mutt_getch(GETCH_NO_FLAGS);
114 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
115
116 if (event.op == OP_REPAINT)
117 window_redraw(NULL);
118
119 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
120 continue;
121
122 if ((event.op == OP_ABORT) || key_is_return(event.ch))
123 {
124 choice = -1;
125 break;
126 }
127
128 char *p = strchr(letters, event.ch);
129 if (p)
130 {
131 choice = p - letters + 1;
132 break;
133 }
134
135 if ((event.ch > '0') && (event.ch <= '9'))
136 {
137 choice = event.ch - '0';
138 if (choice <= mutt_str_len(letters))
139 break;
140 }
141 }
142 // ---------------------------------------------------------------------------
143
144 win = msgcont_pop_window();
145 window_set_focus(old_focus);
146 mutt_window_free(&win);
147
148 return choice;
149}
150
174static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
175 struct ConfigDef *cdef, GetChFlags flags)
176{
177 struct MuttWindow *win = msgwin_new(true);
178 if (!win)
179 return MUTT_ABORT;
180
181 char *yes = N_("yes");
182 char *no = N_("no");
183 char *trans_yes = _(yes);
184 char *trans_no = _(no);
185
186 regex_t reyes = { 0 };
187 regex_t reno = { 0 };
188
189 bool reyes_ok = false;
190 bool reno_ok = false;
191
192#ifdef OpenBSD
193 /* OpenBSD only supports locale C and UTF-8
194 * so there is no suitable base system's locale identification
195 * Remove this code immediately if this situation changes! */
196 char rexyes[16] = "^[+1YyYy]";
197 rexyes[6] = toupper(trans_yes[0]);
198 rexyes[7] = tolower(trans_yes[0]);
199
200 char rexno[16] = "^[-0NnNn]";
201 rexno[6] = toupper(trans_no[0]);
202 rexno[7] = tolower(trans_no[0]);
203
204 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
205 reyes_ok = true;
206
207 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
208 reno_ok = true;
209
210#else
211 char *expr = NULL;
212 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
213 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
214 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
215 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
216#endif
217
218 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
219 {
220 // If all parts of the translation succeeded...
221 yes = trans_yes;
222 no = trans_no;
223 }
224 else
225 {
226 // otherwise, fallback to English
227 if (reyes_ok)
228 {
229 regfree(&reyes);
230 reyes_ok = false;
231 }
232 if (reno_ok)
233 {
234 regfree(&reno);
235 reno_ok = false;
236 }
237 }
238
239 bool show_help_prompt = cdef;
240
241 struct Buffer *text = buf_pool_get();
242 buf_printf(text, "%s ([%s]/%s%s): ", prompt, (def == MUTT_YES) ? yes : no,
243 (def == MUTT_YES) ? no : yes, show_help_prompt ? "/?" : "");
244
247 struct MuttWindow *old_focus = window_set_focus(win);
248
249 struct KeyEvent event = { 0, OP_NULL };
250 window_redraw(NULL);
251 while (true)
252 {
253 event = mutt_getch(flags);
254 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
255 {
256 window_redraw(NULL);
257 mutt_refresh();
258 continue;
259 }
260
261 if (key_is_return(event.ch))
262 break; // Do nothing, use default
263
264 if (event.op == OP_ABORT)
265 {
266 def = MUTT_ABORT;
267 break;
268 }
269
270 char answer[4] = { 0 };
271 answer[0] = event.ch;
272 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(event.ch) == 'y'))
273 {
274 def = MUTT_YES;
275 break;
276 }
277 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(event.ch) == 'n'))
278 {
279 def = MUTT_NO;
280 break;
281 }
282 if (show_help_prompt && (event.ch == '?'))
283 {
284 show_help_prompt = false;
286 buf_printf(text, "$%s - %s\n", cdef->name, cdef->docs);
287
288 char hyphen[128] = { 0 };
289 mutt_str_hyphenate(hyphen, sizeof(hyphen), cdef->name);
290 buf_add_printf(text, "https://neomutt.org/guide/reference#%s\n", hyphen);
291
293
294 buf_printf(text, "%s ([%s]/%s): ", prompt, (def == MUTT_YES) ? yes : no,
295 (def == MUTT_YES) ? no : yes);
297 msgwin_add_text(win, NULL, NULL);
298
299 window_redraw(NULL);
300 mutt_refresh();
301 }
302
303 mutt_beep(false);
304 }
305
306 win = msgcont_pop_window();
307 window_set_focus(old_focus);
308 mutt_window_free(&win);
309
310 if (reyes_ok)
311 regfree(&reyes);
312 if (reno_ok)
313 regfree(&reno);
314
315 buf_pool_release(&text);
316 return def;
317}
318
327enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
328{
329 return mw_yesorno(prompt, def, NULL, GETCH_NO_FLAGS);
330}
331
340enum QuadOption query_yesorno_ignore_macro(const char *prompt, enum QuadOption def)
341{
342 return mw_yesorno(prompt, def, NULL, GETCH_IGNORE_MACRO);
343}
344
355enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
356 struct ConfigSubset *sub, const char *name)
357{
358 struct HashElem *he = cs_subset_create_inheritance(sub, name);
359 struct HashElem *he_base = cs_get_base(he);
360 ASSERT(DTYPE(he_base->type) == DT_BOOL);
361
362 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
363 ASSERT(value != INT_MIN);
364
365 struct ConfigDef *cdef = he_base->data;
366 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
367}
368
379enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
380{
381 struct HashElem *he = cs_subset_create_inheritance(sub, name);
382 struct HashElem *he_base = cs_get_base(he);
383 ASSERT(DTYPE(he_base->type) == DT_QUAD);
384
385 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
386 ASSERT(value != INT_MIN);
387
388 if ((value == MUTT_YES) || (value == MUTT_NO))
389 return value;
390
391 struct ConfigDef *cdef = he_base->data;
392 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
393 return mw_yesorno(prompt, def, cdef, GETCH_NO_FLAGS);
394}
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:56
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:55
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:58
Convenience wrapper for the config headers.
struct HashElem * cs_get_base(struct HashElem *he)
Find the root Config Item.
Definition: set.c:160
void mutt_refresh(void)
Force a refresh of the screen.
Definition: curs_lib.c:78
void mutt_beep(bool force)
Irritate the user.
Definition: curs_lib.c:68
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:174
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:63
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
Convenience wrapper for the gui headers.
Manage keymappings.
uint8_t GetChFlags
Flags for mutt_getch(), e.g. GETCH_NO_FLAGS.
Definition: lib.h:50
#define GETCH_IGNORE_MACRO
Don't use MacroEvents.
Definition: lib.h:52
#define GETCH_NO_FLAGS
No flags are set.
Definition: lib.h:51
@ LL_DEBUG1
Log at debug level 1.
Definition: logging2.h:43
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:849
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:496
#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:634
void mutt_window_free(struct MuttWindow **ptr)
Free a Window and its children.
Definition: mutt_window.c:202
struct MuttWindow * window_set_focus(struct MuttWindow *win)
Set the Window focus.
Definition: mutt_window.c:684
#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:340
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:355
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:379
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:327
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:50
#define ASSERT(COND)
Definition: signal2.h:58
A curses colour and its attributes.
Definition: attr.h:66
String manipulation buffer.
Definition: buffer.h:36
Definition: set.h:64
const char * name
User-visible name.
Definition: set.h:65
const char * docs
One-liner description.
Definition: set.h:84
A set of inherited config items.
Definition: subset.h:47
The item stored in a Hash Table.
Definition: hash.h:43
int type
Type of data stored in Hash Table, e.g. DT_STRING.
Definition: hash.h:44
void * data
User-supplied data.
Definition: hash.h:46
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:258
struct HashElem * cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
Create a Subset config item (inherited)
Definition: subset.c:208
#define DTYPE(t)
Definition: types.h:50
@ DT_BOOL
boolean option
Definition: types.h:32
@ DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:41