NeoMutt  2024-04-16-36-g75b6fb
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 <assert.h>
32#include <ctype.h>
33#include <langinfo.h>
34#include <limits.h>
35#include <stdbool.h>
36#include <stdint.h>
37#include <stdio.h>
38#include <string.h>
39#include "mutt/lib.h"
40#include "config/lib.h"
41#include "gui/lib.h"
42#include "color/lib.h"
43#include "key/lib.h"
44#ifdef HAVE_SYS_PARAM_H
45#include <sys/param.h> // IWYU pragma: keep
46#endif
47
64int mw_multi_choice(const char *prompt, const char *letters)
65{
66 struct MuttWindow *win = msgwin_new(true);
67 if (!win)
68 return -1;
69
70 int choice = 0;
71
72 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
73 const struct AttrColor *ac_prompt = merged_color_overlay(ac_normal,
75
77 {
78 const struct AttrColor *ac_opts = merged_color_overlay(ac_prompt,
80 char *cur = NULL;
81
82 while ((cur = strchr(prompt, '(')))
83 {
84 // write the part between prompt and cur using MT_COLOR_PROMPT
85 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
86
87 if (isalnum(cur[1]) && (cur[2] == ')'))
88 {
89 // we have a single letter within parentheses - MT_COLOR_OPTIONS
90 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
91 prompt = cur + 3;
92 }
93 else
94 {
95 // we have a parenthesis followed by something else
96 msgwin_add_text_n(win, cur, 1, ac_prompt);
97 prompt = cur + 1;
98 }
99 }
100 }
101
102 msgwin_add_text(win, prompt, ac_prompt);
103 msgwin_add_text(win, " ", ac_normal);
104
106 struct MuttWindow *old_focus = window_set_focus(win);
107 window_redraw(win);
108
109 // ---------------------------------------------------------------------------
110 // Event Loop
111 struct KeyEvent event = { 0, OP_NULL };
112 while (true)
113 {
114 event = mutt_getch(GETCH_NO_FLAGS);
115 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
116
117 if (event.op == OP_REPAINT)
118 window_redraw(NULL);
119
120 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
121 continue;
122
123 if ((event.op == OP_ABORT) || key_is_return(event.ch))
124 {
125 choice = -1;
126 break;
127 }
128
129 char *p = strchr(letters, event.ch);
130 if (p)
131 {
132 choice = p - letters + 1;
133 break;
134 }
135
136 if ((event.ch > '0') && (event.ch <= '9'))
137 {
138 choice = event.ch - '0';
139 if (choice <= mutt_str_len(letters))
140 break;
141 }
142 }
143 // ---------------------------------------------------------------------------
144
145 win = msgcont_pop_window();
146 window_set_focus(old_focus);
147 mutt_window_free(&win);
148
149 return choice;
150}
151
174static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
175 struct ConfigDef *cdef)
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(GETCH_NO_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);
330}
331
342enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
343 struct ConfigSubset *sub, const char *name)
344{
345 struct HashElem *he = cs_subset_create_inheritance(sub, name);
346 struct HashElem *he_base = cs_get_base(he);
347 assert(DTYPE(he_base->type) == DT_BOOL);
348
349 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
350 assert(value != INT_MIN);
351
352 struct ConfigDef *cdef = he_base->data;
353 return mw_yesorno(prompt, def, cdef);
354}
355
366enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
367{
368 struct HashElem *he = cs_subset_create_inheritance(sub, name);
369 struct HashElem *he_base = cs_get_base(he);
370 assert(DTYPE(he_base->type) == DT_QUAD);
371
372 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
373 assert(value != INT_MIN);
374
375 if ((value == MUTT_YES) || (value == MUTT_NO))
376 return value;
377
378 struct ConfigDef *cdef = he_base->data;
379 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
380 return mw_yesorno(prompt, def, cdef);
381}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:160
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:203
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:209
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:174
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:64
#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:843
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:490
#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:342
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:366
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:49
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