prompt.c 39.4 KB
Newer Older
1
2
3
4
/* $Id$ */
/**************************************************************************
 *   prompt.c                                                             *
 *                                                                        *
5
6
 *   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007   *
 *   Free Software Foundation, Inc.                                       *
7
8
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
9
 *   the Free Software Foundation; either version 3, or (at your option)  *
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *   any later version.                                                   *
 *                                                                        *
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

26
#include <stdio.h>
27
28
29
30
#include <stdarg.h>
#include <string.h>

static char *prompt = NULL;
31
	/* The prompt string used for statusbar questions. */
32
static size_t statusbar_x = (size_t)-1;
33
	/* The cursor position in answer. */
34
static size_t statusbar_pww = (size_t)-1;
35
	/* The place we want in answer. */
36
37
static size_t old_statusbar_x = (size_t)-1;
	/* The old cursor position in answer, if any. */
38
static size_t old_pww = (size_t)-1;
39
	/* The old place we want in answer, if any. */
40
static bool reset_statusbar_x = FALSE;
41
42
	/* Should we reset the cursor position at the statusbar
	 * prompt? */
43

44
45
46
47
48
49
50
51
/* Read in a character, interpret it as a shortcut or toggle if
 * necessary, and return it.  Set meta_key to TRUE if the character is a
 * meta sequence, set func_key to TRUE if the character is a function
 * key, set s_or_t to TRUE if the character is a shortcut or toggle
 * key, set ran_func to TRUE if we ran a function associated with a
 * shortcut key, and set finished to TRUE if we're done after running
 * or trying to run a function associated with a shortcut key.  If
 * allow_funcs is FALSE, don't actually run any functions associated
52
53
 * with shortcut keys.  refresh_func is the function we will call to
 * refresh the edit window. */
54
int do_statusbar_input(bool *meta_key, bool *func_key, bool *s_or_t,
55
56
	bool *ran_func, bool *finished, bool allow_funcs, void
	(*refresh_func)(void))
57
58
59
60
61
62
63
{
    int input;
	/* The character we read in. */
    static int *kbinput = NULL;
	/* The input buffer. */
    static size_t kbinput_len = 0;
	/* The length of the input buffer. */
64
65
    const sc *s;
    const subnfunc *f;
66
67
68
69
70
71
72
73
74
75
    bool have_shortcut;

    *s_or_t = FALSE;
    *ran_func = FALSE;
    *finished = FALSE;

    /* Read in a character. */
    input = get_kbinput(bottomwin, meta_key, func_key);

#ifndef DISABLE_MOUSE
76
    if (allow_funcs) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
77
78
	/* If we got a mouse click and it was on a shortcut, read in the
	 * shortcut character. */
79
	if (*func_key && input == KEY_MOUSE) {
80
	    if (do_statusbar_mouse() == 1)
81
82
83
84
85
86
87
		input = get_kbinput(bottomwin, meta_key, func_key);
	    else {
		*meta_key = FALSE;
		*func_key = FALSE;
		input = ERR;
	    }
	}
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
88
    }
89
90
91
#endif

    /* Check for a shortcut in the current list. */
92
    s = get_shortcut(currmenu, &input, meta_key, func_key);
93
94
95

    /* If we got a shortcut from the current list, or a "universal"
     * statusbar prompt shortcut, set have_shortcut to TRUE. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
96
97
98
99
100
101
    have_shortcut = (s != NULL || input == NANO_TAB_KEY || input ==
	NANO_ENTER_KEY || input == NANO_REFRESH_KEY || input ==
	NANO_HOME_KEY || input == NANO_END_KEY || input ==
	NANO_BACK_KEY || input == NANO_FORWARD_KEY || input ==
	NANO_BACKSPACE_KEY || input == NANO_DELETE_KEY || input ==
	NANO_CUT_KEY ||
102
#ifndef NANO_TINY
103
	input == NANO_NEXTWORD_KEY ||
104
#endif
105
	(*meta_key && (
106
#ifndef NANO_TINY
107
	input == NANO_PREVWORD_KEY || input == NANO_BRACKET_KEY ||
108
#endif
109
	input == NANO_VERBATIM_KEY)));
110
111
112
113

    /* Set s_or_t to TRUE if we got a shortcut. */
    *s_or_t = have_shortcut;

114
115
    /* If we got a non-high-bit control key, a meta key sequence, or a
     * function key, and it's not a shortcut or toggle, throw it out. */
116
117
    if (!*s_or_t) {
	if (is_ascii_cntrl_char(input) || *meta_key || *func_key) {
118
	    beep();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
119
120
	    *meta_key = FALSE;
	    *func_key = FALSE;
121
	    input = ERR;
122
123
124
	}
    }

125
126
127
128
129
    if (allow_funcs) {
	/* If we got a character, and it isn't a shortcut or toggle,
	 * it's a normal text character.  Display the warning if we're
	 * in view mode, or add the character to the input buffer if
	 * we're not. */
130
	if (input != ERR && !*s_or_t) {
131
132
133
134
	    /* If we're using restricted mode, the filename isn't blank,
	     * and we're at the "Write File" prompt, disable text
	     * input. */
	    if (!ISSET(RESTRICTED) || openfile->filename[0] == '\0' ||
135
		currmenu != MWRITEFILE) {
136
137
138
139
140
141
142
143
144
145
		kbinput_len++;
		kbinput = (int *)nrealloc(kbinput, kbinput_len *
			sizeof(int));
		kbinput[kbinput_len - 1] = input;
	    }
	}

	/* If we got a shortcut, or if there aren't any other characters
	 * waiting after the one we read in, we need to display all the
	 * characters in the input buffer if it isn't empty. */
146
	 if (*s_or_t || get_key_buffer_len() == 0) {
147
148
	    if (kbinput != NULL) {
		/* Display all the characters in the input buffer at
149
		 * once, filtering out control characters. */
150
151
152
153
154
155
156
157
158
159
		char *output = charalloc(kbinput_len + 1);
		size_t i;
		bool got_enter;
			/* Whether we got the Enter key. */

		for (i = 0; i < kbinput_len; i++)
		    output[i] = (char)kbinput[i];
		output[i] = '\0';

		do_statusbar_output(output, kbinput_len, &got_enter,
160
			FALSE);
161
162
163
164
165
166
167
168
169
170
171
172
173

		free(output);

		/* Empty the input buffer. */
		kbinput_len = 0;
		free(kbinput);
		kbinput = NULL;
	    }
	}

	if (have_shortcut) {
	    switch (input) {
		/* Handle the "universal" statusbar prompt shortcuts. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
174
		case NANO_TAB_KEY:
175
176
		case NANO_ENTER_KEY:
		    break;
177
178
179
		case NANO_REFRESH_KEY:
		    total_statusbar_refresh(refresh_func);
		    break;
180
181
182
183
184
		case NANO_CUT_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Cut. */
		    if (!ISSET(RESTRICTED) || openfile->filename[0] ==
185
			'\0' || currmenu != MWRITEFILE)
186
187
			do_statusbar_cut_text();
		    break;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
188
189
190
191
192
193
		case NANO_FORWARD_KEY:
		    do_statusbar_right();
		    break;
		case NANO_BACK_KEY:
		    do_statusbar_left();
		    break;
194
#ifndef NANO_TINY
195
196
197
198
		case NANO_NEXTWORD_KEY:
		    do_statusbar_next_word(FALSE);
		    break;
		case NANO_PREVWORD_KEY:
199
		    if (*meta_key)
200
201
			do_statusbar_prev_word(FALSE);
		    break;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
202
203
204
205
206
207
208
209
210
#endif
		case NANO_HOME_KEY:
		    do_statusbar_home();
		    break;
		case NANO_END_KEY:
		    do_statusbar_end();
		    break;
#ifndef NANO_TINY
		case NANO_BRACKET_KEY:
211
		    if (*meta_key)
212
			do_statusbar_find_bracket();
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
213
		    break;
214
215
#endif
		case NANO_VERBATIM_KEY:
216
		    if (*meta_key) {
217
218
219
220
221
			/* If we're using restricted mode, the filename
			 * isn't blank, and we're at the "Write File"
			 * prompt, disable verbatim input. */
			if (!ISSET(RESTRICTED) ||
				openfile->filename[0] == '\0' ||
222
				currmenu != MWRITEFILE) {
223
224
225
226
227
			    bool got_enter;
				/* Whether we got the Enter key. */

			    do_statusbar_verbatim_input(&got_enter);

228
229
230
231
			    /* If we got the Enter key, remove it from
			     * the input buffer, set input to the key
			     * value for Enter, and set finished to TRUE
			     * to indicate that we're done. */
232
			    if (got_enter) {
233
				get_input(NULL, 1);
234
235
236
237
238
				input = NANO_ENTER_KEY;
				*finished = TRUE;
			    }
			}
		    }
239
		    break;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
240
241
242
243
244
		case NANO_DELETE_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Delete. */
		    if (!ISSET(RESTRICTED) || openfile->filename[0] ==
245
			'\0' || currmenu != MWRITEFILE)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
246
247
248
249
250
251
252
			do_statusbar_delete();
		    break;
		case NANO_BACKSPACE_KEY:
		    /* If we're using restricted mode, the filename
		     * isn't blank, and we're at the "Write File"
		     * prompt, disable Backspace. */
		    if (!ISSET(RESTRICTED) || openfile->filename[0] ==
253
			'\0' || currmenu != MWRITEFILE)
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
254
255
			do_statusbar_backspace();
		    break;
256
257
258
		/* Handle the normal statusbar prompt shortcuts, setting
		 * ran_func to TRUE if we try to run their associated
		 * functions and setting finished to TRUE to indicate
259
260
		 * that we're done after running or trying to run their
		 * associated functions. */
261
		default:
262
263
		    f = sctofunc((sc *) s);
		    if (s->scfunc != NULL &&  s->execute == TRUE) {
264
			*ran_func = TRUE;
265
266
			if (!ISSET(VIEW_MODE) || f->viewok)
			    f->scfunc();
267
268
269
270
271
272
273
274
275
276
		    }
		    *finished = TRUE;
	    }
	}
    }

    return input;
}

#ifndef DISABLE_MOUSE
277
/* Handle a mouse click on the statusbar prompt or the shortcut list. */
278
int do_statusbar_mouse(void)
279
280
{
    int mouse_x, mouse_y;
281
    int retval = get_mouseinput(&mouse_x, &mouse_y, TRUE);
282

283
284
285
286
    /* We can click on the statusbar window text to move the cursor. */
    if (retval == 0 && wmouse_trafo(bottomwin, &mouse_y, &mouse_x,
	FALSE)) {
	size_t start_col;
287

288
	assert(prompt != NULL);
289

290
	start_col = strlenpt(prompt) + 2;
291

292
	/* Move to where the click occurred. */
293
	if (mouse_x >= start_col && mouse_y == 0) {
294
	    size_t pww_save = statusbar_pww;
295

296
	    statusbar_x = actual_x(answer,
297
			get_statusbar_page_start(start_col, start_col +
298
			statusbar_xplustabs()) + mouse_x - start_col);
299
	    statusbar_pww = statusbar_xplustabs();
300

301
302
	    if (need_statusbar_horizontal_update(pww_save))
		update_statusbar_line(answer, statusbar_x);
303
304
305
306
307
308
309
310
	}
    }

    return retval;
}
#endif

/* The user typed output_len multibyte characters.  Add them to the
311
312
313
 * statusbar prompt, setting got_enter to TRUE if we get a newline, and
 * filtering out all ASCII control characters if allow_cntrls is
 * TRUE. */
314
void do_statusbar_output(char *output, size_t output_len, bool
315
	*got_enter, bool allow_cntrls)
316
317
318
319
320
321
322
323
324
325
326
{
    size_t answer_len, i = 0;
    char *char_buf = charalloc(mb_cur_max());
    int char_buf_len;

    assert(answer != NULL);

    answer_len = strlen(answer);
    *got_enter = FALSE;

    while (i < output_len) {
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
327
328
	/* If allow_cntrls is TRUE, convert nulls and newlines
	 * properly. */
329
330
331
	if (allow_cntrls) {
	    /* Null to newline, if needed. */
	    if (output[i] == '\0')
332
		output[i] = '\n';
333
334
	    /* Newline to Enter, if needed. */
	    else if (output[i] == '\n') {
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
		/* Set got_enter to TRUE to indicate that we got the
		 * Enter key, put back the rest of the characters in
		 * output so that they can be parsed and output again,
		 * and get out. */
		*got_enter = TRUE;
		unparse_kbinput(output + i, output_len - i);
		return;
	    }
	}

	/* Interpret the next multibyte character. */
	char_buf_len = parse_mbchar(output + i, char_buf, NULL);

	i += char_buf_len;

350
	/* If allow_cntrls is FALSE, filter out an ASCII control
351
352
353
	 * character. */
	if (!allow_cntrls && is_ascii_cntrl_char(*(output + i -
		char_buf_len)))
354
355
356
357
358
359
360
	    continue;

	/* More dangerousness fun =) */
	answer = charealloc(answer, answer_len + (char_buf_len * 2));

	assert(statusbar_x <= answer_len);

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
361
362
	charmove(answer + statusbar_x + char_buf_len,
		answer + statusbar_x, answer_len - statusbar_x +
363
		char_buf_len);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
364
	strncpy(answer + statusbar_x, char_buf, char_buf_len);
365
366
367
368
369
370
	answer_len += char_buf_len;

	statusbar_x += char_buf_len;
    }

    free(char_buf);
371
372
373

    statusbar_pww = statusbar_xplustabs();

374
    update_statusbar_line(answer, statusbar_x);
375
376
}

377
378
379
380
/* Move to the beginning of the prompt text.  If the SMART_HOME flag is
 * set, move to the first non-whitespace character of the prompt text if
 * we're not already there, or to the beginning of the prompt text if we
 * are. */
381
382
void do_statusbar_home(void)
{
383
384
    size_t pww_save = statusbar_pww;

385
#ifndef NANO_TINY
386
387
388
389
390
391
392
393
    if (ISSET(SMART_HOME)) {
	size_t statusbar_x_save = statusbar_x;

	statusbar_x = indent_length(answer);

	if (statusbar_x == statusbar_x_save ||
		statusbar_x == strlen(answer))
	    statusbar_x = 0;
394
395
396

	statusbar_pww = statusbar_xplustabs();
    } else {
397
398
#endif
	statusbar_x = 0;
399
	statusbar_pww = statusbar_xplustabs();
400
#ifndef NANO_TINY
401
402
403
    }
#endif

404
405
    if (need_statusbar_horizontal_update(pww_save))
	update_statusbar_line(answer, statusbar_x);
406
407
}

408
/* Move to the end of the prompt text. */
409
410
void do_statusbar_end(void)
{
411
412
    size_t pww_save = statusbar_pww;

413
    statusbar_x = strlen(answer);
414
415
    statusbar_pww = statusbar_xplustabs();

416
417
    if (need_statusbar_horizontal_update(pww_save))
	update_statusbar_line(answer, statusbar_x);
418
419
}

420
421
/* Move left one character. */
void do_statusbar_left(void)
422
{
423
    if (statusbar_x > 0) {
424
425
	size_t pww_save = statusbar_pww;

426
	statusbar_x = move_mbleft(answer, statusbar_x);
427
428
	statusbar_pww = statusbar_xplustabs();

429
430
	if (need_statusbar_horizontal_update(pww_save))
	    update_statusbar_line(answer, statusbar_x);
431
    }
432
433
}

434
435
/* Move right one character. */
void do_statusbar_right(void)
436
{
437
    if (statusbar_x < strlen(answer)) {
438
439
	size_t pww_save = statusbar_pww;

440
	statusbar_x = move_mbright(answer, statusbar_x);
441
442
	statusbar_pww = statusbar_xplustabs();

443
444
	if (need_statusbar_horizontal_update(pww_save))
	    update_statusbar_line(answer, statusbar_x);
445
    }
446
447
}

448
/* Backspace over one character. */
449
450
451
452
453
454
455
456
void do_statusbar_backspace(void)
{
    if (statusbar_x > 0) {
	do_statusbar_left();
	do_statusbar_delete();
    }
}

457
/* Delete one character. */
458
459
void do_statusbar_delete(void)
{
460
461
    statusbar_pww = statusbar_xplustabs();

462
463
464
465
466
467
468
469
470
471
472
473
    if (answer[statusbar_x] != '\0') {
	int char_buf_len = parse_mbchar(answer + statusbar_x, NULL,
		NULL);
	size_t line_len = strlen(answer + statusbar_x);

	assert(statusbar_x < strlen(answer));

	charmove(answer + statusbar_x, answer + statusbar_x +
		char_buf_len, strlen(answer) - statusbar_x -
		char_buf_len + 1);

	null_at(&answer, statusbar_x + line_len - char_buf_len);
474
475

	update_statusbar_line(answer, statusbar_x);
476
477
478
    }
}

479
/* Move text from the prompt into oblivion. */
480
481
482
483
void do_statusbar_cut_text(void)
{
    assert(answer != NULL);

484
#ifndef NANO_TINY
485
486
487
488
489
490
    if (ISSET(CUT_TO_END))
	null_at(&answer, statusbar_x);
    else {
#endif
	null_at(&answer, 0);
	statusbar_x = 0;
491
	statusbar_pww = statusbar_xplustabs();
492
#ifndef NANO_TINY
493
494
    }
#endif
495

496
    update_statusbar_line(answer, statusbar_x);
497
498
}

499
#ifndef NANO_TINY
500
501
502
/* Move to the next word in the prompt text.  If allow_punct is TRUE,
 * treat punctuation as part of a word.  Return TRUE if we started on a
 * word, and FALSE otherwise. */
503
504
bool do_statusbar_next_word(bool allow_punct)
{
505
    size_t pww_save = statusbar_pww;
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
    char *char_mb;
    int char_mb_len;
    bool end_line = FALSE, started_on_word = FALSE;

    assert(answer != NULL);

    char_mb = charalloc(mb_cur_max());

    /* Move forward until we find the character after the last letter of
     * the current word. */
    while (!end_line) {
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL);

	/* If we've found it, stop moving forward through the current
	 * line. */
	if (!is_word_mbchar(char_mb, allow_punct))
	    break;

	/* If we haven't found it, then we've started on a word, so set
	 * started_on_word to TRUE. */
	started_on_word = TRUE;

	if (answer[statusbar_x] == '\0')
	    end_line = TRUE;
	else
	    statusbar_x += char_mb_len;
    }

    /* Move forward until we find the first letter of the next word. */
    if (answer[statusbar_x] == '\0')
	end_line = TRUE;
    else
	statusbar_x += char_mb_len;

    while (!end_line) {
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL);

	/* If we've found it, stop moving forward through the current
	 * line. */
	if (is_word_mbchar(char_mb, allow_punct))
	    break;

	if (answer[statusbar_x] == '\0')
	    end_line = TRUE;
	else
	    statusbar_x += char_mb_len;
    }

    free(char_mb);

556
557
    statusbar_pww = statusbar_xplustabs();

558
559
    if (need_statusbar_horizontal_update(pww_save))
	update_statusbar_line(answer, statusbar_x);
560

561
562
563
564
    /* Return whether we started on a word. */
    return started_on_word;
}

565
/* Move to the previous word in the prompt text.  If allow_punct is
566
567
568
569
 * TRUE, treat punctuation as part of a word.  Return TRUE if we started
 * on a word, and FALSE otherwise. */
bool do_statusbar_prev_word(bool allow_punct)
{
570
    size_t pww_save = statusbar_pww;
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
    char *char_mb;
    int char_mb_len;
    bool begin_line = FALSE, started_on_word = FALSE;

    assert(answer != NULL);

    char_mb = charalloc(mb_cur_max());

    /* Move backward until we find the character before the first letter
     * of the current word. */
    while (!begin_line) {
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL);

	/* If we've found it, stop moving backward through the current
	 * line. */
	if (!is_word_mbchar(char_mb, allow_punct))
	    break;

	/* If we haven't found it, then we've started on a word, so set
	 * started_on_word to TRUE. */
	started_on_word = TRUE;

	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);
    }

    /* Move backward until we find the last letter of the previous
     * word. */
    if (statusbar_x == 0)
	begin_line = TRUE;
    else
	statusbar_x = move_mbleft(answer, statusbar_x);

    while (!begin_line) {
	char_mb_len = parse_mbchar(answer + statusbar_x, char_mb, NULL);

	/* If we've found it, stop moving backward through the current
	 * line. */
	if (is_word_mbchar(char_mb, allow_punct))
	    break;

	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);
    }

    /* If we've found it, move backward until we find the character
     * before the first letter of the previous word. */
    if (!begin_line) {
	if (statusbar_x == 0)
	    begin_line = TRUE;
	else
	    statusbar_x = move_mbleft(answer, statusbar_x);

	while (!begin_line) {
	    char_mb_len = parse_mbchar(answer + statusbar_x, char_mb,
		NULL);

	    /* If we've found it, stop moving backward through the
	     * current line. */
	    if (!is_word_mbchar(char_mb, allow_punct))
		break;

	    if (statusbar_x == 0)
		begin_line = TRUE;
	    else
		statusbar_x = move_mbleft(answer, statusbar_x);
	}

	/* If we've found it, move forward to the first letter of the
	 * previous word. */
	if (!begin_line)
	    statusbar_x += char_mb_len;
    }

    free(char_mb);

651
652
    statusbar_pww = statusbar_xplustabs();

653
654
    if (need_statusbar_horizontal_update(pww_save))
	update_statusbar_line(answer, statusbar_x);
655

656
657
658
    /* Return whether we started on a word. */
    return started_on_word;
}
659
#endif /* !NANO_TINY */
660

661
662
/* Get verbatim input.  Set got_enter to TRUE if we got the Enter key as
 * part of the verbatim input. */
663
664
665
666
667
668
669
670
671
672
673
674
void do_statusbar_verbatim_input(bool *got_enter)
{
    int *kbinput;
    size_t kbinput_len, i;
    char *output;

    *got_enter = FALSE;

    /* Read in all the verbatim characters. */
    kbinput = get_verbatim_kbinput(bottomwin, &kbinput_len);

    /* Display all the verbatim characters at once, not filtering out
675
     * control characters. */
676
677
678
679
680
681
    output = charalloc(kbinput_len + 1);

    for (i = 0; i < kbinput_len; i++)
	output[i] = (char)kbinput[i];
    output[i] = '\0';

682
    do_statusbar_output(output, kbinput_len, got_enter, TRUE);
683
684
685
686

    free(output);
}

687
#ifndef NANO_TINY
688
689
690
691
/* Search for a match to one of the two characters in bracket_set.  If
 * reverse is TRUE, search backwards for the leftmost bracket.
 * Otherwise, search forwards for the rightmost bracket.  Return TRUE if
 * we found a match, and FALSE otherwise. */
692
693
694
695
696
bool find_statusbar_bracket_match(bool reverse, const char
	*bracket_set)
{
    const char *rev_start = NULL, *found = NULL;

697
    assert(mbstrlen(bracket_set) == 2);
698
699
700
701
702
703
704
705
706
707
708
709
710
711

    /* rev_start might end up 1 character before the start or after the
     * end of the line.  This won't be a problem because we'll skip over
     * it below in that case. */
    rev_start = reverse ? answer + (statusbar_x - 1) : answer +
	(statusbar_x + 1);

    while (TRUE) {
	/* Look for either of the two characters in bracket_set.
	 * rev_start can be 1 character before the start or after the
	 * end of the line.  In either case, just act as though no match
	 * is found. */
	found = ((rev_start > answer && *(rev_start - 1) == '\0') ||
		rev_start < answer) ? NULL : (reverse ?
712
713
		mbrevstrpbrk(answer, bracket_set, rev_start) :
		mbstrpbrk(rev_start, bracket_set));
714
715
716
717

	/* We've found a potential match. */
	if (found != NULL)
	    break;
718
719
720
721

	/* We've reached the start or end of the statusbar text, so
	 * get out. */
	return FALSE;
722
723
724
725
726
727
728
729
730
    }

    /* We've definitely found something. */
    statusbar_x = found - answer;
    statusbar_pww = statusbar_xplustabs();

    return TRUE;
}

731
732
/* Search for a match to the bracket at the current cursor position, if
 * there is one. */
733
734
735
void do_statusbar_find_bracket(void)
{
    size_t statusbar_x_save, pww_save;
736
    const char *ch;
737
	/* The location in matchbrackets of the bracket at the current
738
	 * cursor position. */
739
740
741
    int ch_len;
	/* The length of ch in bytes. */
    const char *wanted_ch;
742
743
	/* The location in matchbrackets of the bracket complementing
	 * the bracket at the current cursor position. */
744
745
746
    int wanted_ch_len;
	/* The length of wanted_ch in bytes. */
    char *bracket_set;
747
	/* The pair of characters in ch and wanted_ch. */
748
749
    size_t i;
	/* Generic loop variable. */
750
    size_t matchhalf;
751
752
753
754
755
	/* The number of single-byte characters in one half of
	 * matchbrackets. */
    size_t mbmatchhalf;
	/* The number of multibyte characters in one half of
	 * matchbrackets. */
756
757
758
759
    size_t count = 1;
	/* The initial bracket count. */
    bool reverse;
	/* The direction we search. */
760
761
    char *found_ch;
	/* The character we find. */
762

763
    assert(mbstrlen(matchbrackets) % 2 == 0);
764

765
    ch = answer + statusbar_x;
766

767
    if (ch == '\0' || (ch = mbstrchr(matchbrackets, ch)) == NULL)
768
769
770
771
772
773
	return;

    /* Save where we are. */
    statusbar_x_save = statusbar_x;
    pww_save = statusbar_pww;

774
    /* If we're on an opening bracket, which must be in the first half
775
     * of matchbrackets, we want to search forwards for a closing
776
     * bracket.  If we're on a closing bracket, which must be in the
777
     * second half of matchbrackets, we want to search backwards for an
778
     * opening bracket. */
779
780
781
782
783
784
785
    matchhalf = 0;
    mbmatchhalf = mbstrlen(matchbrackets) / 2;

    for (i = 0; i < mbmatchhalf; i++)
	matchhalf += parse_mbchar(matchbrackets + matchhalf, NULL,
		NULL);

786
    reverse = ((ch - matchbrackets) >= matchhalf);
787
788

    /* If we're on an opening bracket, set wanted_ch to the character
789
790
791
     * that's matchhalf characters after ch.  If we're on a closing
     * bracket, set wanted_ch to the character that's matchhalf
     * characters before ch. */
792
793
    wanted_ch = ch;

794
    while (mbmatchhalf > 0) {
795
	if (reverse)
796
797
	    wanted_ch = matchbrackets + move_mbleft(matchbrackets,
		wanted_ch - matchbrackets);
798
799
800
	else
	    wanted_ch += move_mbright(wanted_ch, 0);

801
	mbmatchhalf--;
802
803
804
805
806
807
808
809
810
811
812
813
    }

    ch_len = parse_mbchar(ch, NULL, NULL);
    wanted_ch_len = parse_mbchar(wanted_ch, NULL, NULL);

    /* Fill bracket_set in with the values of ch and wanted_ch. */
    bracket_set = charalloc((mb_cur_max() * 2) + 1);
    strncpy(bracket_set, ch, ch_len);
    strncpy(bracket_set + ch_len, wanted_ch, wanted_ch_len);
    null_at(&bracket_set, ch_len + wanted_ch_len);

    found_ch = charalloc(mb_cur_max() + 1);
814
815

    while (TRUE) {
816
	if (find_statusbar_bracket_match(reverse, bracket_set)) {
817
818
	    /* If we found an identical bracket, increment count.  If we
	     * found a complementary bracket, decrement it. */
819
820
	    parse_mbchar(answer + statusbar_x, found_ch, NULL);
	    count += (strncmp(found_ch, ch, ch_len) == 0) ? 1 : -1;
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836

	    /* If count is zero, we've found a matching bracket.  Update
	     * the statusbar prompt and get out. */
	    if (count == 0) {
		if (need_statusbar_horizontal_update(pww_save))
		    update_statusbar_line(answer, statusbar_x);
		break;
	    }
	} else {
	    /* We didn't find either an opening or closing bracket.
	     * Restore where we were, and get out. */
	    statusbar_x = statusbar_x_save;
	    statusbar_pww = pww_save;
	    break;
	}
    }
837
838
839
840

    /* Clean up. */
    free(bracket_set);
    free(found_ch);
841
842
843
}
#endif /* !NANO_TINY */

844
/* Return the placewewant associated with statusbar_x, i.e. the
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
 * zero-based column position of the cursor.  The value will be no
 * smaller than statusbar_x. */
size_t statusbar_xplustabs(void)
{
    return strnlenpt(answer, statusbar_x);
}

/* nano scrolls horizontally within a line in chunks.  This function
 * returns the column number of the first character displayed in the
 * statusbar prompt when the cursor is at the given column with the
 * prompt ending at start_col.  Note that (0 <= column -
 * get_statusbar_page_start(column) < COLS). */
size_t get_statusbar_page_start(size_t start_col, size_t column)
{
    if (column == start_col || column < COLS - 1)
	return 0;
    else
	return column - start_col - (column - start_col) % (COLS -
		start_col - 1);
}

866
867
868
/* Put the cursor in the statusbar prompt at statusbar_x. */
void reset_statusbar_cursor(void)
{
869
    size_t start_col = strlenpt(prompt) + 2;
870
871
    size_t xpt = statusbar_xplustabs();

872
    wmove(bottomwin, 0, start_col + xpt -
873
874
875
	get_statusbar_page_start(start_col, start_col + xpt));
}

876
877
/* Repaint the statusbar when getting a character in
 * get_prompt_string().  The statusbar text line will be displayed
878
 * starting with curranswer[index]. */
879
void update_statusbar_line(const char *curranswer, size_t index)
880
{
881
    size_t start_col, page_start;
882
883
    char *expanded;

884
    assert(prompt != NULL && index <= strlen(curranswer));
885

886
    start_col = strlenpt(prompt) + 2;
887
888
    index = strnlenpt(curranswer, index);
    page_start = get_statusbar_page_start(start_col, start_col + index);
889

890
    wattron(bottomwin, reverse_attr);
891
892
893
894
895
896
897

    blank_statusbar();

    mvwaddnstr(bottomwin, 0, 0, prompt, actual_x(prompt, COLS - 2));
    waddch(bottomwin, ':');
    waddch(bottomwin, (page_start == 0) ? ' ' : '$');

898
899
    expanded = display_string(curranswer, page_start, COLS - start_col -
	1, FALSE);
900
901
902
    waddstr(bottomwin, expanded);
    free(expanded);

903
    wattroff(bottomwin, reverse_attr);
904

905
    reset_statusbar_cursor();
906
    wnoutrefresh(bottomwin);
907
908
}

909
/* Return TRUE if we need an update after moving horizontally, and FALSE
910
911
912
 * otherwise.  We need one if pww_save and statusbar_pww are on
 * different pages. */
bool need_statusbar_horizontal_update(size_t pww_save)
913
{
914
    size_t start_col = strlenpt(prompt) + 2;
915

916
    return get_statusbar_page_start(start_col, start_col + pww_save) !=
917
918
919
	get_statusbar_page_start(start_col, start_col + statusbar_pww);
}

920
921
922
923
924
925
926
927
/* Unconditionally redraw the entire screen, and then refresh it using
 * refresh_func(). */
void total_statusbar_refresh(void (*refresh_func)(void))
{
    total_redraw();
    refresh_func();
}

928
929
/* Get a string of input at the statusbar prompt.  This should only be
 * called from do_prompt(). */
930
931
932
933
934
int get_prompt_string(bool allow_tabs,
#ifndef DISABLE_TABCOMP
	bool allow_files,
#endif
	const char *curranswer,
935
#ifndef NANO_TINY
936
937
	filestruct **history_list,
#endif
938
	void (*refresh_func)(void), int menu
939
940
941
942
943
#ifndef DISABLE_TABCOMP
	, bool *list
#endif
	)
{
944
    int kbinput = ERR;
945
    bool meta_key, func_key, s_or_t, ran_func, finished;
946
947
948
949
950
    size_t curranswer_len;
#ifndef DISABLE_TABCOMP
    bool tabbed = FALSE;
	/* Whether we've pressed Tab. */
#endif
951
#ifndef NANO_TINY
952
953
954
955
956
957
958
959
960
961
962
963
    char *history = NULL;
	/* The current history string. */
    char *magichistory = NULL;
	/* The temporary string typed at the bottom of the history, if
	 * any. */
#ifndef DISABLE_TABCOMP
    int last_kbinput = ERR;
	/* The key we pressed before the current key. */
    size_t complete_len = 0;
	/* The length of the original string that we're trying to
	 * tab complete, if any. */
#endif
964
#endif /* !NANO_TINY */
965
966
967
968

    answer = mallocstrcpy(answer, curranswer);
    curranswer_len = strlen(answer);

969
970
971
972
973
974
975
976
977
978
    /* If reset_statusbar_x is TRUE, restore statusbar_x and
     * statusbar_pww to what they were before this prompt.  Then, if
     * statusbar_x is uninitialized or past the end of curranswer, put
     * statusbar_x at the end of the string and update statusbar_pww
     * based on it.  We do these things so that the cursor position
     * stays at the right place if a prompt-changing toggle is pressed,
     * or if this prompt was started from another prompt and we cancel
     * out of it. */
    if (reset_statusbar_x) {
	statusbar_x = old_statusbar_x;
979
	statusbar_pww = old_pww;
980
981
982
    }

    if (statusbar_x == (size_t)-1 || statusbar_x > curranswer_len) {
983
	statusbar_x = curranswer_len;
984
985
	statusbar_pww = statusbar_xplustabs();
    }
986

987
988
989
990
991
    currmenu = menu;

#ifdef DEBUG
fprintf(stderr, "get_prompt_string: answer = \"%s\", statusbar_x = %d\n", answer, statusbar_x);
#endif
992

993
    update_statusbar_line(answer, statusbar_x);
994
995
996
997
998
999
1000

    /* Refresh the edit window and the statusbar before getting
     * input. */
    wnoutrefresh(edit);
    wnoutrefresh(bottomwin);

    /* If we're using restricted mode, we aren't allowed to change the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1001
1002
1003
1004
     * name of the current file once it has one, because that would
     * allow writing to files not specified on the command line.  In
     * this case, disable all keys that would change the text if the
     * filename isn't blank and we're at the "Write File" prompt. */
1005
    while ((kbinput = do_statusbar_input(&meta_key, &func_key, &s_or_t,
1006
1007
	&ran_func, &finished, TRUE, refresh_func)) != NANO_CANCEL_KEY &&
	kbinput != NANO_ENTER_KEY) {
1008
1009
1010
1011
1012
1013
1014
1015
1016
	assert(statusbar_x <= strlen(answer));

#ifndef DISABLE_TABCOMP
	if (kbinput != NANO_TAB_KEY)
	    tabbed = FALSE;
#endif

	switch (kbinput) {
#ifndef DISABLE_TABCOMP
1017
#ifndef NANO_TINY
1018
	    case NANO_TAB_KEY:
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
		if (history_list != NULL) {
		    if (last_kbinput != NANO_TAB_KEY)
			complete_len = strlen(answer);

		    if (complete_len > 0) {
			answer = mallocstrcpy(answer,
				get_history_completion(history_list,
				answer, complete_len));
			statusbar_x = strlen(answer);
		    }
		} else
1030
#endif /* !NANO_TINY */
1031
		if (allow_tabs)
1032
		    answer = input_tab(answer, allow_files,
1033
			&statusbar_x, &tabbed, refresh_func, list);
1034
1035

		update_statusbar_line(answer, statusbar_x);
1036
		break;
1037
#endif /* !DISABLE_TABCOMP */
1038
#ifndef NANO_TINY
1039
	    case NANO_PREVLINE_KEY:
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
		if (history_list != NULL) {
		    /* If we're scrolling up at the bottom of the
		     * history list and answer isn't blank, save answer
		     * in magichistory. */
		    if ((*history_list)->next == NULL &&
			answer[0] != '\0')
			magichistory = mallocstrcpy(magichistory,
				answer);

		    /* Get the older search from the history list and
		     * save it in answer.  If there is no older search,
		     * don't do anything. */
		    if ((history =
			get_history_older(history_list)) != NULL) {
			answer = mallocstrcpy(answer, history);
			statusbar_x = strlen(answer);
		    }

1058
1059
		    update_statusbar_line(answer, statusbar_x);

1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
		    /* This key has a shortcut list entry when it's used
		     * to move to an older search, which means that
		     * finished has been set to TRUE.  Set it back to
		     * FALSE here, so that we aren't kicked out of the
		     * statusbar prompt. */
		    finished = FALSE;
		}
		break;
	    case NANO_NEXTLINE_KEY:
		if (history_list != NULL) {
		    /* Get the newer search from the history list and
		     * save it in answer.  If there is no newer search,
		     * don't do anything. */
		    if ((history =
			get_history_newer(history_list)) != NULL) {
			answer = mallocstrcpy(answer, history);
			statusbar_x = strlen(answer);
		    }

		    /* If, after scrolling down, we're at the bottom of
		     * the history list, answer is blank, and
		     * magichistory is set, save magichistory in
		     * answer. */
		    if ((*history_list)->next == NULL &&
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1084
			*answer == '\0' && magichistory != NULL) {
1085
1086
1087
			answer = mallocstrcpy(answer, magichistory);
			statusbar_x = strlen(answer);
		    }
1088
1089

		    update_statusbar_line(answer, statusbar_x);
1090
1091

		    /* This key has a shortcut list entry when it's used
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1092
		     * to move to a newer search, which means that
1093
1094
1095
1096
		     * finished has been set to TRUE.  Set it back to
		     * FALSE here, so that we aren't kicked out of the
		     * statusbar prompt. */
		    finished = FALSE;
1097
1098
		}
		break;
1099
#endif /* !NANO_TINY */
1100
1101
1102
1103
	    case NANO_HELP_KEY:
		update_statusbar_line(answer, statusbar_x);

		/* This key has a shortcut list entry when it's used to
1104
1105
1106
1107
1108
		 * go to the help browser or display a message
		 * indicating that help is disabled, which means that
		 * finished has been set to TRUE.  Set it back to FALSE
		 * here, so that we aren't kicked out of the statusbar
		 * prompt. */
1109
1110
		finished = FALSE;
		break;
1111
1112
1113
1114
1115
1116
1117
1118
	}

	/* If we have a shortcut with an associated function, break out
	 * if we're finished after running or trying to run the
	 * function. */
	if (finished)
	    break;

1119
#if !defined(NANO_TINY) && !defined(DISABLE_TABCOMP)
1120
1121
1122
	last_kbinput = kbinput;
#endif

1123
	reset_statusbar_cursor();
1124
	wnoutrefresh(bottomwin);
1125
    }
1126

1127

1128
#ifndef NANO_TINY
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
    /* Set the current position in the history list to the bottom and
     * free magichistory, if we need to. */
    if (history_list != NULL) {
	history_reset(*history_list);

	if (magichistory != NULL)
	    free(magichistory);
    }
#endif

1139

1140
1141
1142
1143
    /* We've finished putting in an answer or run a normal shortcut's
     * associated function, so reset statusbar_x and statusbar_pww.  If
     * we've finished putting in an answer, reset the statusbar cursor
     * position too. */
1144
    if (kbinput == NANO_CANCEL_KEY || kbinput == NANO_ENTER_KEY ||
1145
	ran_func) {
1146
	statusbar_x = old_statusbar_x;
1147
1148
1149
1150
1151
1152
1153
1154
1155
	statusbar_pww = old_pww;

	if (!ran_func)
	    reset_statusbar_x = TRUE;
    /* Otherwise, we're still putting in an answer or a shortcut with
     * an associated function, so leave the statusbar cursor position
     * alone. */
    } else
	reset_statusbar_x = FALSE;
1156
1157
1158
1159

    return kbinput;
}

1160
1161
1162
1163
/* Ask a question on the statusbar.  The prompt will be stored in the
 * static prompt, which should be NULL initially, and the answer will be
 * stored in the answer global.  Returns -1 on aborted enter, -2 on a
 * blank string, and 0 otherwise, the valid shortcut key caught.
1164
1165
1166
 * curranswer is any editable text that we want to put up by default,
 * and refresh_func is the function we want to call to refresh the edit
 * window.
1167
1168
 *
 * The allow_tabs parameter indicates whether we should allow tabs to be
1169
1170
1171
1172
1173
1174
1175
 * interpreted.  The allow_files parameter indicates whether we should
 * allow all files (as opposed to just directories) to be tab
 * completed. */
int do_prompt(bool allow_tabs,
#ifndef DISABLE_TABCOMP
	bool allow_files,
#endif
1176
	int menu, const char *curranswer,
1177
#ifndef NANO_TINY
1178
1179
	filestruct **history_list,
#endif
1180
	void (*refresh_func)(void), const char *msg, ...)
1181
1182
1183
1184
1185
1186
1187
{
    va_list ap;
    int retval;
#ifndef DISABLE_TABCOMP
    bool list = FALSE;
#endif

1188
1189
1190
1191
    /* prompt has been freed and set to NULL unless the user resized
     * while at the statusbar prompt. */
    if (prompt != NULL)
	free(prompt);
1192

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1193
    prompt = charalloc(((COLS - 4) * mb_cur_max()) + 1);
1194

1195
    bottombars(menu);
1196
1197
1198
1199
1200
1201

    va_start(ap, msg);
    vsnprintf(prompt, (COLS - 4) * mb_cur_max(), msg, ap);
    va_end(ap);
    null_at(&prompt, actual_x(prompt, COLS - 4));

1202
1203
1204
1205
1206
    retval = get_prompt_string(allow_tabs,
#ifndef DISABLE_TABCOMP
	allow_files,
#endif
	curranswer,
1207
#ifndef NANO_TINY
1208
	history_list,
1209
#endif
1210
	refresh_func, menu
1211
#ifndef DISABLE_TABCOMP
1212
	, &list
1213
#endif
1214
	);
1215

1216
1217
1218
    free(prompt);
    prompt = NULL;

1219
1220
    /* We're done with the prompt, so save the statusbar cursor
     * position. */
1221
    old_statusbar_x = statusbar_x;
1222
    old_pww = statusbar_pww;
1223

1224
1225
    /* If we left the prompt via Cancel or Enter, set the return value
     * properly. */
1226
1227
1228
1229
1230
    switch (retval) {
	case NANO_CANCEL_KEY:
	    retval = -1;
	    break;
	case NANO_ENTER_KEY:
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1231
	    retval = (*answer == '\0') ? -2 : 0;
1232
1233
1234
	    break;
    }

1235

1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
    blank_statusbar();
    wnoutrefresh(bottomwin);

#ifdef DEBUG
    fprintf(stderr, "answer = \"%s\"\n", answer);
#endif

#ifndef DISABLE_TABCOMP
    /* If we've done tab completion, there might be a list of filename
     * matches on the edit window at this point.  Make sure that they're
     * cleared off. */
    if (list)
1248
	refresh_func();
1249
1250
1251
1252
1253
#endif

    return retval;
}

1254
/* This function forces a reset of the statusbar cursor position.  It
1255
 * should be called when we get out of all statusbar prompts. */
1256
void do_prompt_abort(void)
1257
{
1258
1259
1260
1261
    /* Uninitialize the old cursor position in answer. */
    old_statusbar_x = (size_t)-1;
    old_pww = (size_t)-1;

1262
    reset_statusbar_x = TRUE;
1263
}
1264
1265
1266
1267

/* Ask a simple Yes/No (and optionally All) question, specified in msg,
 * on the statusbar.  Return 1 for Yes, 0 for No, 2 for All (if all is
 * TRUE when passed in), and -1 for Cancel. */
1268
int do_yesno_prompt(bool all, const char *msg)
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
{
    int ok = -2, width = 16;
    const char *yesstr;		/* String of Yes characters accepted. */
    const char *nostr;		/* Same for No. */
    const char *allstr;		/* And All, surprise! */

    assert(msg != NULL);

    /* yesstr, nostr, and allstr are strings of any length.  Each string
     * consists of all single-byte characters accepted as valid
     * characters for that value.  The first value will be the one
1280
1281
     * displayed in the shortcuts. */
    /* TRANSLATORS: For the next three strings, if possible, specify
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
1282
1283
     * the single-byte shortcuts for both your language and English.
     * For example, in French: "OoYy" for "Oui". */
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
    yesstr = _("Yy");
    nostr = _("Nn");
    allstr = _("Aa");

    if (!ISSET(NO_HELP)) {
	char shortstr[3];
		/* Temp string for Yes, No, All. */

	if (COLS < 32)
	    width = COLS / 2;

	/* Clear the shortcut list from the bottom of the screen. */
	blank_bottombars();

	sprintf(shortstr, " %c", yesstr[0]);
	wmove(bottomwin, 1, 0);
	onekey(shortstr, _("Yes"), width);

	if (all) {
	    wmove(bottomwin, 1, width);
	    shortstr[1] = allstr[0];
	    onekey(shortstr, _("All"), width);
	}

	wmove(bottomwin, 2, 0);
	shortstr[1] = nostr[0];
	onekey(shortstr, _("No"), width);

	wmove(bottomwin, 2, 16);
	onekey("^C", _("Cancel"), width);
    }

1316
    wattron(bottomwin, reverse_attr);
1317
1318
1319
1320

    blank_statusbar();
    mvwaddnstr(bottomwin, 0, 0, msg, actual_x(msg, COLS - 1));

1321
    wattroff(bottomwin, reverse_attr);
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336

    /* Refresh the edit window and the statusbar before getting
     * input. */
    wnoutrefresh(edit);
    wnoutrefresh(bottomwin);

    do {
	int kbinput;
	bool meta_key, func_key;
#ifndef DISABLE_MOUSE
	int mouse_x, mouse_y;
#endif

	kbinput = get_kbinput(bottomwin, &meta_key, &func_key);

1337
1338
1339
1340
	switch (kbinput) {
	    case NANO_CANCEL_KEY:
		ok = -1;
		break;
1341
#ifndef DISABLE_MOUSE
1342
	    case KEY_MOUSE:
1343
1344
		/* We can click on the Yes/No/All shortcut list to
		 * select an answer. */
1345
		if (get_mouseinput(&mouse_x, &mouse_y, FALSE) == 0 &&
1346
			wmouse_trafo(bottomwin, &mouse_y, &mouse_x,
1347
1348
			FALSE) && mouse_x < (width * 2) &&
			mouse_y > 0) {
1349
		    int x = mouse_x / width;
1350
1351
1352
			/* Calculate the x-coordinate relative to the
			 * two columns of the Yes/No/All shortcuts in
			 * bottomwin. */
1353
		    int y = mouse_y - 1;
1354
1355
			/* Calculate the y-coordinate relative to the
			 * beginning of the Yes/No/All shortcuts in
1356
			 * bottomwin, i.e. with the sizes of topwin,
1357
1358
1359
			 * edit, and the first line of bottomwin
			 * subtracted out. */

1360
		    assert(0 <= x && x <= 1 && 0 <= y && y <= 1);
1361

1362
1363
1364
		    /* x == 0 means they clicked Yes or No.  y == 0
		     * means Yes or All. */
		    ok = -2 * x * y + x - y + 1;
1365

1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
		    if (ok == 2 && !all)
			ok = -2;
		}
		break;
#endif /* !DISABLE_MOUSE */
	    case NANO_REFRESH_KEY:
		total_redraw();
		continue;
	    default:
		/* Look for the kbinput in the Yes, No and (optionally)
		 * All strings. */
		if (strchr(yesstr, kbinput) != NULL)
		    ok = 1;
		else if (strchr(nostr, kbinput) != NULL)
		    ok = 0;
		else if (all && strchr(allstr, kbinput) != NULL)
		    ok = 2;
1383
1384
1385
1386
1387
	}
    } while (ok == -2);

    return ok;
}