help.c 16.9 KB
Newer Older
1
2
3
/**************************************************************************
 *   help.c                                                               *
 *                                                                        *
4
 *   Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,  *
5
 *   2009, 2010, 2011, 2013, 2014 Free Software Foundation, Inc.          *
6
7
 *   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 *
8
 *   the Free Software Foundation; either version 3, or (at your option)  *
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 *   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.                                                     *
 *                                                                        *
 **************************************************************************/

23
#include "proto.h"
24
25
26
27
28
29
30
31
32
33

#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifndef DISABLE_HELP

static char *help_text = NULL;
	/* The text displayed in the help window. */

34
35
36
37
static char *end_of_intro = NULL;
	/* The point in the help text where the introductory paragraphs end
	 * and the shortcut descriptions begin. */

38
39
/* Our main help-viewer function. */
void do_help(void)
40
{
41
    int kbinput = ERR;
42
    bool old_no_help = ISSET(NO_HELP);
43
    size_t line = 0;
44
45
	/* The line number in help_text of the first displayed help
	 * line.  This variable is zero-based. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
46
    size_t last_line = 0;
47
48
	/* The line number in help_text of the last help line.  This
	 * variable is zero-based. */
49
    int oldmenu = currmenu;
50
	/* The menu we were called from. */
51
    const char *ptr;
52
	/* The current line of the help text. */
53
54
    size_t old_line = (size_t)-1;
	/* The line we were on before the current line. */
55
56
    functionptrtype func;
	/* The function of the key the user typed in. */
57

58
    /* Don't show a cursor in the help screen. */
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    curs_set(0);
    blank_edit();
    blank_statusbar();

    /* Set help_text as the string to display. */
    help_init();

    assert(help_text != NULL);

    if (ISSET(NO_HELP)) {
	/* Make sure that the help screen's shortcut list will actually
	 * be displayed. */
	UNSET(NO_HELP);
	window_init();
    }

75
    bottombars(MHELP);
76
    wnoutrefresh(bottomwin);
77

78
79
80
    while (TRUE) {
	size_t i;

81
	ptr = help_text;
82

83
	/* Find the line number of the last line of the help text. */
84
85
86
87
88
89
90
91
	for (last_line = 0; *ptr != '\0'; last_line++) {
	    ptr += help_line_len(ptr);
	    if (*ptr == '\n')
		ptr++;
	}

	if (last_line > 0)
	    last_line--;
92

93
94
	/* Redisplay if the text was scrolled or an invalid key was pressed. */
	if (line != old_line || kbinput == ERR) {
95
96
97
98
	    blank_edit();

	    ptr = help_text;

99
	    /* Advance in the text to the first line to be displayed. */
100
101
102
103
104
105
	    for (i = 0; i < line; i++) {
		ptr += help_line_len(ptr);
		if (*ptr == '\n')
		    ptr++;
	    }

106
	    /* Now display as many lines as the window will hold. */
107
108
109
110
111
112
113
114
115
116
117
118
	    for (i = 0; i < editwinrows && *ptr != '\0'; i++) {
		size_t j = help_line_len(ptr);

		mvwaddnstr(edit, i, 0, ptr, j);
		ptr += j;
		if (*ptr == '\n')
		    ptr++;
	    }
	}

	wnoutrefresh(edit);

119
120
	old_line = line;

121
	lastmessage = HUSH;
122

123
	kbinput = get_kbinput(edit);
124

125
126
127
#ifndef NANO_TINY
	if (kbinput == KEY_WINCH) {
	    kbinput = ERR;
128
	    continue;    /* Redraw the screen. */
129
130
131
	}
#endif

132
#ifndef DISABLE_MOUSE
133
	if (kbinput == KEY_MOUSE) {
134
135
136
	    int mouse_x, mouse_y;
	    get_mouseinput(&mouse_x, &mouse_y, TRUE);
	    continue;    /* Redraw the screen. */
137
138
139
	}
#endif

140
	func = parse_help_input(&kbinput);
141

142
	if (func == total_refresh) {
143
	    total_redraw();
144
	} else if (func == do_page_up) {
145
146
147
148
	    if (line > editwinrows - 2)
		line -= editwinrows - 2;
	    else
		line = 0;
149
	} else if (func == do_page_down) {
150
151
	    if (line + (editwinrows - 1) < last_line)
		line += editwinrows - 2;
152
	} else if (func == do_up_void) {
153
154
	    if (line > 0)
		line--;
155
	} else if (func == do_down_void) {
156
157
	    if (line + (editwinrows - 1) < last_line)
		line++;
158
	} else if (func == do_first_line) {
159
	    line = 0;
160
	} else if (func == do_last_line) {
161
162
	    if (line + (editwinrows - 1) < last_line)
		line = last_line - (editwinrows - 1);
163
	} else if (func == do_exit) {
164
	    /* Exit from the help viewer. */
165
	    break;
166
167
	} else
	    unbound_key(kbinput);
168
    }
169
170
171
172

    if (old_no_help) {
	blank_bottombars();
	wnoutrefresh(bottomwin);
173
	currmenu = oldmenu;
174
175
176
	SET(NO_HELP);
	window_init();
    } else
177
	bottombars(oldmenu);
178

179
180
181
182
183
184
#ifndef DISABLE_BROWSER
    if (oldmenu == MBROWSER || oldmenu == MWHEREISFILE || oldmenu == MGOTODIR)
	browser_refresh();
    else
#endif
	edit_refresh();
185

186
    /* We're exiting from the help screen. */
187
188
189
    free(help_text);
}

190
191
/* Allocate space for the help text for the current menu, and concatenate
 * the different pieces of text into it. */
192
193
void help_init(void)
{
194
195
196
197
198
    size_t allocsize = 0;
	/* Space needed for help_text. */
    const char *htx[3];
	/* Untranslated help introduction.  We break it up into three chunks
	 * in case the full string is too long for the compiler to handle. */
199
    char *ptr;
200
201
202
    const subnfunc *f;
    const sc *s;

203
#ifndef NANO_TINY
204
205
206
207
208
209
    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);

    UNSET(WHITESPACE_DISPLAY);
#endif

    /* First, set up the initial help text for the current function. */
210
    if (currmenu == MWHEREIS || currmenu == MREPLACE || currmenu == MREPLACEWITH) {
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	htx[0] = N_("Search Command Help Text\n\n "
		"Enter the words or characters you would like to "
		"search for, and then press Enter.  If there is a "
		"match for the text you entered, the screen will be "
		"updated to the location of the nearest match for the "
		"search string.\n\n The previous search string will be "
		"shown in brackets after the search prompt.  Hitting "
		"Enter without entering any text will perform the "
		"previous search.  ");
	htx[1] = N_("If you have selected text with the mark and then "
		"search to replace, only matches in the selected text "
		"will be replaced.\n\n The following function keys are "
		"available in Search mode:\n\n");
	htx[2] = NULL;
225
    } else if (currmenu == MGOTOLINE) {
226
227
228
229
230
231
232
233
	htx[0] = N_("Go To Line Help Text\n\n "
		"Enter the line number that you wish to go to and hit "
		"Enter.  If there are fewer lines of text than the "
		"number you entered, you will be brought to the last "
		"line of the file.\n\n The following function keys are "
		"available in Go To Line mode:\n\n");
	htx[1] = NULL;
	htx[2] = NULL;
234
    } else if (currmenu == MINSERTFILE) {
235
236
237
238
239
240
241
242
243
244
245
246
247
248
	htx[0] = N_("Insert File Help Text\n\n "
		"Type in the name of a file to be inserted into the "
		"current file buffer at the current cursor "
		"location.\n\n If you have compiled nano with multiple "
		"file buffer support, and enable multiple file buffers "
		"with the -F or --multibuffer command line flags, the "
		"Meta-F toggle, or a nanorc file, inserting a file "
		"will cause it to be loaded into a separate buffer "
		"(use Meta-< and > to switch between file buffers).  ");
	htx[1] = N_("If you need another blank buffer, do not enter "
		"any filename, or type in a nonexistent filename at "
		"the prompt and press Enter.\n\n The following "
		"function keys are available in Insert File mode:\n\n");
	htx[2] = NULL;
249
    } else if (currmenu == MWRITEFILE) {
250
251
252
253
254
255
256
257
258
259
260
261
262
	htx[0] = N_("Write File Help Text\n\n "
		"Type the name that you wish to save the current file "
		"as and press Enter to save the file.\n\n If you have "
		"selected text with the mark, you will be prompted to "
		"save only the selected portion to a separate file.  To "
		"reduce the chance of overwriting the current file with "
		"just a portion of it, the current filename is not the "
		"default in this mode.\n\n The following function keys "
		"are available in Write File mode:\n\n");
	htx[1] = NULL;
	htx[2] = NULL;
    }
#ifndef DISABLE_BROWSER
263
    else if (currmenu == MBROWSER) {
264
265
266
267
268
269
270
271
272
273
274
275
	htx[0] = N_("File Browser Help Text\n\n "
		"The file browser is used to visually browse the "
		"directory structure to select a file for reading "
		"or writing.  You may use the arrow keys or Page Up/"
		"Down to browse through the files, and S or Enter to "
		"choose the selected file or enter the selected "
		"directory.  To move up one level, select the "
		"directory called \"..\" at the top of the file "
		"list.\n\n The following function keys are available "
		"in the file browser:\n\n");
	htx[1] = NULL;
	htx[2] = NULL;
276
    } else if (currmenu == MWHEREISFILE) {
277
278
279
280
281
282
283
284
	htx[0] = N_("Browser Search Command Help Text\n\n "
		"Enter the words or characters you would like to "
		"search for, and then press Enter.  If there is a "
		"match for the text you entered, the screen will be "
		"updated to the location of the nearest match for the "
		"search string.\n\n The previous search string will be "
		"shown in brackets after the search prompt.  Hitting "
		"Enter without entering any text will perform the "
285
286
		"previous search.\n\n");
	htx[1] = N_(" The following function keys are available in "
287
288
		"Browser Search mode:\n\n");
	htx[2] = NULL;
289
    } else if (currmenu == MGOTODIR) {
290
291
292
293
294
295
296
297
298
299
	htx[0] = N_("Browser Go To Directory Help Text\n\n "
		"Enter the name of the directory you would like to "
		"browse to.\n\n If tab completion has not been "
		"disabled, you can use the Tab key to (attempt to) "
		"automatically complete the directory name.\n\n The "
		"following function keys are available in Browser Go "
		"To Directory mode:\n\n");
	htx[1] = NULL;
	htx[2] = NULL;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
300
#endif /* !DISABLE_BROWSER */
301
#ifndef DISABLE_SPELLER
302
    else if (currmenu == MSPELL) {
303
304
305
306
307
308
309
	htx[0] = N_("Spell Check Help Text\n\n "
		"The spell checker checks the spelling of all text in "
		"the current file.  When an unknown word is "
		"encountered, it is highlighted and a replacement can "
		"be edited.  It will then prompt to replace every "
		"instance of the given misspelled word in the current "
		"file, or, if you have selected text with the mark, in "
310
		"the selected text.\n\n The following function keys "
311
312
313
314
		"are available in Spell Check mode:\n\n");
	htx[1] = NULL;
	htx[2] = NULL;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
315
#endif /* !DISABLE_SPELLER */
316
#ifndef NANO_TINY
317
    else if (currmenu == MEXTCMD) {
318
	htx[0] = N_("Execute Command Help Text\n\n "
319
		"This mode allows you to insert the output of a "
320
		"command run by the shell into the current buffer (or "
321
		"a new buffer in multiple file buffer mode).  If you "
322
		"need another blank buffer, do not enter any "
323
324
		"command.\n\n The following function keys are "
		"available in Execute Command mode:\n\n");
325
326
327
	htx[1] = NULL;
	htx[2] = NULL;
    }
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
328
#endif /* !NANO_TINY */
329
330
    else {
	/* Default to the main help list. */
331
	htx[0] = N_("Main nano help text\n\n "
332
333
334
335
336
337
338
339
		"The nano editor is designed to emulate the "
		"functionality and ease-of-use of the UW Pico text "
		"editor.  There are four main sections of the editor.  "
		"The top line shows the program version, the current "
		"filename being edited, and whether or not the file "
		"has been modified.  Next is the main editor window "
		"showing the file being edited.  The status line is "
		"the third line from the bottom and shows important "
340
341
		"messages.  ");
	htx[1] = N_("The bottom two lines show the most commonly used "
342
343
344
345
346
347
		"shortcuts in the editor.\n\n Shortcuts are written as "
		"follows: Control-key sequences are notated with a '^' "
		"and can be entered either by using the Ctrl key or "
		"pressing the Esc key twice.  Meta-key sequences are "
		"notated with 'M-' and can be entered using either the "
		"Alt, Cmd, or Esc key, depending on your keyboard setup.  ");
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
	htx[2] = N_("Also, pressing Esc twice and then typing a "
		"three-digit decimal number from 000 to 255 will enter "
		"the character with the corresponding value.  The "
		"following keystrokes are available in the main editor "
		"window.  Alternative keys are shown in "
		"parentheses:\n\n");
    }

    htx[0] = _(htx[0]);
    if (htx[1] != NULL)
	htx[1] = _(htx[1]);
    if (htx[2] != NULL)
	htx[2] = _(htx[2]);

    allocsize += strlen(htx[0]);
    if (htx[1] != NULL)
	allocsize += strlen(htx[1]);
    if (htx[2] != NULL)
	allocsize += strlen(htx[2]);

368
369
370
    /* Calculate the length of the shortcut help text.  Each entry has
     * one or two keys, which fill 16 columns, plus translated text,
     * plus one or two \n's. */
371
372
373
    for (f = allfuncs; f != NULL; f = f->next)
	if (f->menus & currmenu)
	    allocsize += (16 * mb_cur_max()) + strlen(f->help) + 2;
374

375
#ifndef NANO_TINY
376
    /* If we're on the main list, we also count the toggle help text.
377
378
     * Each entry has "M-%c\t\t", five chars which fill 16 columns,
     * plus a space, plus translated text, plus one or two '\n's. */
379
    if (currmenu == MMAIN) {
380
	size_t endis_len = strlen(_("enable/disable"));
381

382
	for (s = sclist; s != NULL; s = s->next)
383
	    if (s->scfunc == do_toggle_void)
384
		allocsize += strlen(_(flagtostr(s->toggle))) + endis_len + 8;
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
    }
#endif

    /* Allocate space for the help text. */
    help_text = charalloc(allocsize + 1);

    /* Now add the text we want. */
    strcpy(help_text, htx[0]);
    if (htx[1] != NULL)
	strcat(help_text, htx[1]);
    if (htx[2] != NULL)
	strcat(help_text, htx[2]);

    ptr = help_text + strlen(help_text);

400
401
402
    /* Remember this end-of-introduction, start-of-shortcuts. */
    end_of_intro = ptr;

403
404
    /* Now add our shortcut info. */
    for (f = allfuncs; f != NULL; f = f->next) {
405
	int scsfound = 0;
406

407
	if ((f->menus & currmenu) == 0)
408
	    continue;
409

410
	/* Let's simply show the first two shortcuts from the list. */
411
	for (s = sclist; s != NULL; s = s->next) {
412

413
	    if ((s->menus & currmenu) == 0)
414
415
		continue;

416
	    if (s->scfunc == f->scfunc) {
417
		scsfound++;
418
419
420
421
422
423
424
		/* Make the first column narrower (6) than the second (10),
		 * but allow it to spill into the second, for "M-Space". */
		if (scsfound == 1) {
		    sprintf(ptr, "%s              ", s->keystr);
		    ptr += 6;
		} else {
		    ptr += sprintf(ptr, "(%s)\t", s->keystr);
425
		    break;
426
		}
427
	    }
428
	}
429
430
431
432
433

	if (scsfound == 0)
	    ptr += sprintf(ptr, "\t\t");
	else if (scsfound == 1)
	    ptr += 10;
434

435
	/* The shortcut's help text. */
436
	ptr += sprintf(ptr, "%s\n", _(f->help));
437

438
	if (f->blank_after)
439
	    ptr += sprintf(ptr, "\n");
440
441
    }

442
#ifndef NANO_TINY
443
    /* And the toggles... */
444
445
446
447
    if (currmenu == MMAIN) {
	int maximum = 0, counter = 0;

	/* First see how many toggles there are. */
448
	for (s = sclist; s != NULL; s = s->next)
449
	    maximum = (s->toggle && s->ordinal > maximum) ? s->ordinal : maximum;
450
451
452
453

	/* Now show them in the original order. */
	while (counter < maximum) {
	    counter++;
454
	    for (s = sclist; s != NULL; s = s->next)
455
		if (s->toggle && s->ordinal == counter) {
456
		    ptr += sprintf(ptr, "%s\t\t%s %s\n", (s->menus == MMAIN ? s->keystr : ""),
457
				_(flagtostr(s->toggle)), _("enable/disable"));
458
459
		    if (s->toggle == NO_COLOR_SYNTAX || s->toggle == TABS_TO_SPACES)
			ptr += sprintf(ptr, "\n");
460
		    break;
461
		}
462
	}
463
    }
464

465
466
    if (old_whitespace)
	SET(WHITESPACE_DISPLAY);
467
#endif /* !NANO_TINY */
468

469
    /* If all went well, we didn't overwrite the allocated space. */
470
471
472
    assert(strlen(help_text) <= allocsize + 1);
}

473
474
475
/* Return the function that is bound to the given key, accepting certain
 * plain characters too, for consistency with the file browser. */
functionptrtype parse_help_input(int *kbinput)
476
{
477
    if (!meta_key) {
478
	switch (*kbinput) {
479
	    case ' ':
480
		return do_page_down;
481
	    case '-':
482
		return do_page_up;
483
484
	    case 'E':
	    case 'e':
485
		return do_exit;
486
487
	}
    }
488
    return func_from_key(kbinput);
489
490
}

491
/* Calculate the displayable length of the help-text line starting at ptr. */
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
492
493
size_t help_line_len(const char *ptr)
{
494
    size_t wrapping_point = (COLS > 24) ? COLS - 1 : 24;
495
	/* The target width for wrapping long lines. */
496
497
498
499
    ssize_t wrap_location;
	/* Actual position where the line can be wrapped. */
    size_t length = 0;
	/* Full length of the line, until the first newline. */
500
501
502

    /* Avoid overwide paragraphs in the introductory text. */
    if (ptr < end_of_intro && COLS > 74)
503
	wrapping_point = 74;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
504

505
    wrap_location = break_line(ptr, wrapping_point, TRUE);
506

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
507
    /* Get the length of the entire line up to a null or a newline. */
508
509
510
511
512
513
514
515
516
517
    while (*(ptr + length) != '\0' && *(ptr + length) != '\n')
	length = move_mbright(ptr, length);

    /* If the entire line will just fit the screen, don't wrap it. */
    if (strnlenpt(ptr, length) <= wrapping_point + 1)
	return length;
    else if (wrap_location > 0)
	return wrap_location;
    else
	return 0;
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
518
519
}

520
#endif /* !DISABLE_HELP */
521

522
/* Start the help viewer. */
523
524
525
void do_help_void(void)
{
#ifndef DISABLE_HELP
526
    do_help();
527
528
#else
    if (currmenu == MMAIN)
529
	say_there_is_no_help();
530
531
    else
	beep();
532
#endif /* !DISABLE_HELP */
533
}