NeoMutt
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
question.c
Go to the documentation of this file.
1
29#include "config.h"
30#include <assert.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
60int mw_multi_choice(const char *prompt, const char *letters)
61{
62 struct MuttWindow *win = msgwin_new(true);
63 if (!win)
64 return -1;
65
66 int choice = 0;
67
68 const struct AttrColor *ac_opts = NULL;
70 {
71 const struct AttrColor *ac_base = simple_color_get(MT_COLOR_NORMAL);
73
75 ac_opts = merged_color_overlay(ac_base, ac_opts);
76 }
77
78 const struct AttrColor *ac_normal = simple_color_get(MT_COLOR_NORMAL);
79 const struct AttrColor *ac_prompt = simple_color_get(MT_COLOR_PROMPT);
80
81 if (ac_opts)
82 {
83 char *cur = NULL;
84
85 while ((cur = strchr(prompt, '(')))
86 {
87 // write the part between prompt and cur using MT_COLOR_PROMPT
88 msgwin_add_text_n(win, prompt, cur - prompt, ac_prompt);
89
90 if (isalnum(cur[1]) && (cur[2] == ')'))
91 {
92 // we have a single letter within parentheses - MT_COLOR_OPTIONS
93 msgwin_add_text_n(win, cur + 1, 1, ac_opts);
94 prompt = cur + 3;
95 }
96 else
97 {
98 // we have a parenthesis followed by something else
99 msgwin_add_text_n(win, cur, 1, ac_prompt);
100 prompt = cur + 1;
101 }
102 }
103 }
104
105 msgwin_add_text(win, prompt, ac_prompt);
106 msgwin_add_text(win, " ", ac_normal);
107
109 struct MuttWindow *old_focus = window_set_focus(win);
110 window_redraw(win);
111
112 // ---------------------------------------------------------------------------
113 // Event Loop
114 struct KeyEvent event = { 0, OP_NULL };
115 while (true)
116 {
117 event = mutt_getch(GETCH_NO_FLAGS);
118 mutt_debug(LL_DEBUG1, "mw_multi_choice: EVENT(%d,%d)\n", event.ch, event.op);
119
120 if (event.op == OP_REPAINT)
121 window_redraw(NULL);
122
123 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
124 continue;
125
126 if ((event.op == OP_ABORT) || key_is_return(event.ch))
127 {
128 choice = -1;
129 break;
130 }
131
132 char *p = strchr(letters, event.ch);
133 if (p)
134 {
135 choice = p - letters + 1;
136 break;
137 }
138
139 if ((event.ch > '0') && (event.ch <= '9'))
140 {
141 choice = event.ch - '0';
142 if (choice <= mutt_str_len(letters))
143 break;
144 }
145 }
146 // ---------------------------------------------------------------------------
147
148 win = msgcont_pop_window();
149 window_set_focus(old_focus);
150 mutt_window_free(&win);
151
152 return choice;
153}
154
177static enum QuadOption mw_yesorno(const char *prompt, enum QuadOption def,
178 struct ConfigDef *cdef)
179{
180 struct MuttWindow *win = msgwin_new(true);
181 if (!win)
182 return MUTT_ABORT;
183
184 char *yes = N_("yes");
185 char *no = N_("no");
186 char *trans_yes = _(yes);
187 char *trans_no = _(no);
188
189 regex_t reyes = { 0 };
190 regex_t reno = { 0 };
191
192 bool reyes_ok = false;
193 bool reno_ok = false;
194
195#ifdef OpenBSD
196 /* OpenBSD only supports locale C and UTF-8
197 * so there is no suitable base system's locale identification
198 * Remove this code immediately if this situation changes! */
199 char rexyes[16] = "^[+1YyYy]";
200 rexyes[6] = toupper(trans_yes[0]);
201 rexyes[7] = tolower(trans_yes[0]);
202
203 char rexno[16] = "^[-0NnNn]";
204 rexno[6] = toupper(trans_no[0]);
205 rexno[7] = tolower(trans_no[0]);
206
207 if (REG_COMP(&reyes, rexyes, REG_NOSUB) == 0)
208 reyes_ok = true;
209
210 if (REG_COMP(&reno, rexno, REG_NOSUB) == 0)
211 reno_ok = true;
212
213#else
214 char *expr = NULL;
215 reyes_ok = (expr = nl_langinfo(YESEXPR)) && (expr[0] == '^') &&
216 (REG_COMP(&reyes, expr, REG_NOSUB) == 0);
217 reno_ok = (expr = nl_langinfo(NOEXPR)) && (expr[0] == '^') &&
218 (REG_COMP(&reno, expr, REG_NOSUB) == 0);
219#endif
220
221 if ((yes != trans_yes) && (no != trans_no) && reyes_ok && reno_ok)
222 {
223 // If all parts of the translation succeeded...
224 yes = trans_yes;
225 no = trans_no;
226 }
227 else
228 {
229 // otherwise, fallback to English
230 if (reyes_ok)
231 {
232 regfree(&reyes);
233 reyes_ok = false;
234 }
235 if (reno_ok)
236 {
237 regfree(&reno);
238 reno_ok = false;
239 }
240 }
241
242 bool show_help_prompt = cdef;
243
244 struct Buffer *text = buf_pool_get();
245 buf_printf(text, "%s ([%s]/%s%s): ", prompt, (def == MUTT_YES) ? yes : no,
246 (def == MUTT_YES) ? no : yes, show_help_prompt ? "/?" : "");
247
250 struct MuttWindow *old_focus = window_set_focus(win);
251
252 struct KeyEvent event = { 0, OP_NULL };
253 window_redraw(NULL);
254 while (true)
255 {
256 event = mutt_getch(GETCH_NO_FLAGS);
257 if ((event.op == OP_TIMEOUT) || (event.op == OP_REPAINT))
258 {
259 window_redraw(NULL);
260 mutt_refresh();
261 continue;
262 }
263
264 if (key_is_return(event.ch))
265 break; // Do nothing, use default
266
267 if (event.op == OP_ABORT)
268 {
269 def = MUTT_ABORT;
270 break;
271 }
272
273 char answer[4] = { 0 };
274 answer[0] = event.ch;
275 if (reyes_ok ? (regexec(&reyes, answer, 0, 0, 0) == 0) : (tolower(event.ch) == 'y'))
276 {
277 def = MUTT_YES;
278 break;
279 }
280 if (reno_ok ? (regexec(&reno, answer, 0, 0, 0) == 0) : (tolower(event.ch) == 'n'))
281 {
282 def = MUTT_NO;
283 break;
284 }
285 if (show_help_prompt && (event.ch == '?'))
286 {
287 show_help_prompt = false;
289 buf_printf(text, "$%s - %s\n", cdef->name, cdef->docs);
290
291 char hyphen[128] = { 0 };
292 mutt_str_hyphenate(hyphen, sizeof(hyphen), cdef->name);
293 buf_add_printf(text, "https://neomutt.org/guide/reference#%s\n", hyphen);
294
296
297 buf_printf(text, "%s ([%s]/%s): ", prompt, (def == MUTT_YES) ? yes : no,
298 (def == MUTT_YES) ? no : yes);
300 msgwin_add_text(win, NULL, NULL);
301
302 window_redraw(NULL);
303 mutt_refresh();
304 }
305
306 mutt_beep(false);
307 }
308
309 win = msgcont_pop_window();
310 window_set_focus(old_focus);
311 mutt_window_free(&win);
312
313 if (reyes_ok)
314 regfree(&reyes);
315 if (reno_ok)
316 regfree(&reno);
317
318 buf_pool_release(&text);
319 return def;
320}
321
330enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
331{
332 return mw_yesorno(prompt, def, NULL);
333}
334
345enum QuadOption query_yesorno_help(const char *prompt, enum QuadOption def,
346 struct ConfigSubset *sub, const char *name)
347{
348 struct HashElem *he = cs_subset_create_inheritance(sub, name);
349 struct HashElem *he_base = cs_get_base(he);
350 assert(DTYPE(he_base->type) == DT_BOOL);
351
352 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
353 assert(value != INT_MIN);
354
355 struct ConfigDef *cdef = he_base->data;
356 return mw_yesorno(prompt, def, cdef);
357}
358
369enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
370{
371 struct HashElem *he = cs_subset_create_inheritance(sub, name);
372 struct HashElem *he_base = cs_get_base(he);
373 assert(DTYPE(he_base->type) == DT_QUAD);
374
375 intptr_t value = cs_subset_he_native_get(sub, he, NULL);
376 assert(value != INT_MIN);
377
378 if ((value == MUTT_YES) || (value == MUTT_NO))
379 return value;
380
381 struct ConfigDef *cdef = he_base->data;
382 enum QuadOption def = (value == MUTT_ASKYES) ? MUTT_YES : MUTT_NO;
383 return mw_yesorno(prompt, def, cdef);
384}
int buf_printf(struct Buffer *buf, const char *fmt,...)
Format a string overwriting a Buffer.
Definition: buffer.c:173
int buf_add_printf(struct Buffer *buf, const char *fmt,...)
Format a string appending a Buffer.
Definition: buffer.c:216
static const char * buf_string(const struct Buffer *buf)
Convert a buffer to a const char * "string".
Definition: buffer.h:93
Color and attribute parsing.
bool simple_color_is_set(enum ColorId cid)
Is the object coloured?
Definition: simple.c:102
struct AttrColor * simple_color_get(enum ColorId cid)
Get the colour of an object by its ID.
Definition: simple.c:81
@ MT_COLOR_OPTIONS
Options in prompt.
Definition: color.h:58
@ MT_COLOR_NORMAL
Plain text.
Definition: color.h:57
@ MT_COLOR_PROMPT
Question/user input.
Definition: color.h:60
Convenience wrapper for the config headers.
struct HashElem * cs_get_base(struct HashElem *he)
Find the root Config Item.
Definition: set.c:157
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:221
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:177
int mw_multi_choice(const char *prompt, const char *letters)
Offer the user a multiple choice question -.
Definition: question.c:60
#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:52
@ 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:109
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:373
void msgwin_clear_text(struct MuttWindow *win)
Clear the text in the Message Window.
Definition: msgwin.c:515
void msgwin_add_text(struct MuttWindow *win, const char *text, const struct AttrColor *ac_color)
Add text to the Message Window.
Definition: msgwin.c:421
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:452
void msgwin_set_text(struct MuttWindow *win, const char *text, enum ColorId color)
Set the text for the Message Window.
Definition: msgwin.c:486
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:1068
size_t mutt_str_len(const char *a)
Calculate the length of a string, safely.
Definition: string.c:568
#define key_is_return(ch)
Definition: mutt_curses.h:51
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:34
#define OP_REPAINT
Repaint is needed.
Definition: opcodes.h:32
#define OP_ABORT
$abort_key pressed (Ctrl-G)
Definition: opcodes.h:35
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:345
enum QuadOption query_quadoption(const char *prompt, struct ConfigSubset *sub, const char *name)
Ask the user a quad-question.
Definition: question.c:369
enum QuadOption query_yesorno(const char *prompt, enum QuadOption def)
Ask the user a Yes/No question.
Definition: question.c:330
#define REG_COMP(preg, regex, cflags)
Compile a regular expression.
Definition: regex3.h:53
A curses colour and its attributes.
Definition: attr.h:35
String manipulation buffer.
Definition: buffer.h:34
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: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:249
struct HashElem * cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
Create a Subset config item (inherited)
Definition: subset.c:199
#define DTYPE(x)
Mask for the Data Type.
Definition: types.h:45
#define DT_QUAD
quad-option (no/yes/ask-no/ask-yes)
Definition: types.h:37
#define DT_BOOL
boolean option
Definition: types.h:30