NeoMutt  2024-04-25-76-g20fe7b
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
173static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
174 struct ConfigDef *cdef)
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] = toupper(trans_yes[0]);
197 rexyes[7] = tolower(trans_yes[0]);
198
199 char rexno[16] = "^[-0NnNn]";
200 rexno[6] = toupper(trans_no[0]);
201 rexno[7] = 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(GETCH_NO_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) : (tolower(event.ch) == 'y'))
272 {
273 def = MUTT_YES;
274 break;
275 }
276 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (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);
329}
330
341enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
342 struct ConfigSubset *sub, const char *name)
343{
344 struct HashElem *he = cs_subset_create_inheritance(sub, name);
345 struct HashElem *he_base = cs_get_base(he);
346 ASSERT(DTYPE(he_base->type) == DT_BOOL);
347
348 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
349 ASSERT(value != INT_MIN);
350
351 struct ConfigDef *cdef = he_base->data;
352 return mw_yesorno(prompt, def, cdef);
353}
354
365enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
366{
367 struct HashElem *he = cs_subset_create_inheritance(sub, name);
368 struct HashElem *he_base = cs_get_base(he);
369 ASSERT(DTYPE(he_base->type) == DT_QUAD);
370
371 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
372 ASSERT(value != INT_MIN);
373
374 if ((value == MUTT_YES) || (value == MUTT_NO))
375 return value;
376
377 struct ConfigDef *cdef = he_base->data;
378 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
379 return mw_yesorno(prompt, def, cdef);
380}
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:109
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:88
@ MT_COLOR_OPTIONS
Options in prompt.
Definition: color.h:60
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:59
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:62
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)
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:63
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
Convenience wrapper for the gui headers.
Manage keymappings.
#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:81
void buf_pool_release(struct Buffer **ptr)
Return a Buffer to the pool.
Definition: pool.c:94
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_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:341
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:365
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:49
#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