1/* $Id: help.c,v 1.58.2.1 2007/04/19 03:15:04 dolorous Exp $ */ 2/************************************************************************** 3 * help.c * 4 * * 5 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Chris Allegretta * 6 * Copyright (C) 2005, 2006, 2007 David Lawrence Ramsey * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2, or (at your option) * 10 * any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, but * 13 * WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 15 * General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software * 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 20 * 02110-1301, USA. * 21 * * 22 **************************************************************************/ 23 24#include "proto.h" 25 26#include <stdio.h> 27#include <string.h> 28#include <ctype.h> 29 30#ifndef DISABLE_HELP 31 32static char *help_text = NULL; 33 /* The text displayed in the help window. */ 34 35/* Our main help browser function. refresh_func is the function we will 36 * call to refresh the edit window. */ 37void do_help(void (*refresh_func)(void)) 38{ 39 int kbinput = ERR; 40 bool meta_key, func_key, old_no_help = ISSET(NO_HELP); 41 bool abort = FALSE; 42 /* Whether we should abort the help browser. */ 43 size_t line = 0; 44 /* The line number in help_text of the first displayed help 45 * line. This variable is zero-based. */ 46 size_t last_line = 0; 47 /* The line number in help_text of the last help line. This 48 * variable is zero-based. */ 49#ifndef DISABLE_MOUSE 50 const shortcut *oldshortcut = currshortcut; 51 /* The current shortcut list. */ 52#endif 53 const char *ptr; 54 /* The current line of the help text. */ 55 size_t old_line = (size_t)-1; 56 /* The line we were on before the current line. */ 57 58 curs_set(0); 59 blank_edit(); 60 wattroff(bottomwin, reverse_attr); 61 blank_statusbar(); 62 63 /* Set help_text as the string to display. */ 64 help_init(); 65 66 assert(help_text != NULL); 67 68#ifndef DISABLE_MOUSE 69 /* Set currshortcut to allow clicking on the help screen's shortcut 70 * list, after help_init() is called. */ 71 currshortcut = help_list; 72#endif 73 74 if (ISSET(NO_HELP)) { 75 /* Make sure that the help screen's shortcut list will actually 76 * be displayed. */ 77 UNSET(NO_HELP); 78 window_init(); 79 } 80 81 bottombars(help_list); 82 wnoutrefresh(bottomwin); 83 84 /* Get the last line of the help text. */ 85 ptr = help_text; 86 87 for (; *ptr != '\0'; last_line++) { 88 ptr += help_line_len(ptr); 89 if (*ptr == '\n') 90 ptr++; 91 } 92 if (last_line > 0) 93 last_line--; 94 95 while (!abort) { 96 size_t i; 97 98 /* Display the help text if we don't have a key, or if the help 99 * text has moved. */ 100 if (kbinput == ERR || line != old_line) { 101 blank_edit(); 102 103 ptr = help_text; 104 105 /* Calculate where in the text we should be, based on the 106 * page. */ 107 for (i = 0; i < line; i++) { 108 ptr += help_line_len(ptr); 109 if (*ptr == '\n') 110 ptr++; 111 } 112 113 for (i = 0; i < editwinrows && *ptr != '\0'; i++) { 114 size_t j = help_line_len(ptr); 115 116 mvwaddnstr(edit, i, 0, ptr, j); 117 ptr += j; 118 if (*ptr == '\n') 119 ptr++; 120 } 121 } 122 123 wnoutrefresh(edit); 124 125 old_line = line; 126 127 kbinput = get_kbinput(edit, &meta_key, &func_key); 128 parse_help_input(&kbinput, &meta_key, &func_key); 129 130 switch (kbinput) { 131#ifndef DISABLE_MOUSE 132 case KEY_MOUSE: 133 { 134 int mouse_x, mouse_y; 135 136 get_mouseinput(&mouse_x, &mouse_y, TRUE); 137 } 138 break; 139#endif 140 /* Redraw the screen. */ 141 case NANO_REFRESH_KEY: 142 total_redraw(); 143 break; 144 case NANO_PREVPAGE_KEY: 145 if (line > editwinrows - 2) 146 line -= editwinrows - 2; 147 else 148 line = 0; 149 break; 150 case NANO_NEXTPAGE_KEY: 151 if (line + (editwinrows - 1) < last_line) 152 line += editwinrows - 2; 153 break; 154 case NANO_PREVLINE_KEY: 155 if (line > 0) 156 line--; 157 break; 158 case NANO_NEXTLINE_KEY: 159 if (line + (editwinrows - 1) < last_line) 160 line++; 161 break; 162 case NANO_FIRSTLINE_METAKEY: 163 if (meta_key) 164 line = 0; 165 break; 166 case NANO_LASTLINE_METAKEY: 167 if (meta_key) { 168 if (line + (editwinrows - 1) < last_line) 169 line = last_line - (editwinrows - 1); 170 } 171 break; 172 /* Abort the help browser. */ 173 case NANO_EXIT_KEY: 174 abort = TRUE; 175 break; 176 } 177 } 178 179#ifndef DISABLE_MOUSE 180 currshortcut = oldshortcut; 181#endif 182 183 if (old_no_help) { 184 blank_bottombars(); 185 wnoutrefresh(bottomwin); 186 SET(NO_HELP); 187 window_init(); 188 } else 189 bottombars(currshortcut); 190 191 curs_set(1); 192 refresh_func(); 193 194 /* The help_init() at the beginning allocated help_text. Since 195 * help_text has now been written to the screen, we don't need it 196 * anymore. */ 197 free(help_text); 198 help_text = NULL; 199} 200 201/* Start the help browser for the edit window. */ 202void do_help_void(void) 203{ 204 do_help(&edit_refresh); 205} 206 207#ifndef DISABLE_BROWSER 208/* Start the help browser for the file browser. */ 209void do_browser_help(void) 210{ 211 do_help(&browser_refresh); 212} 213#endif 214 215/* This function allocates help_text, and stores the help string in it. 216 * help_text should be NULL initially. */ 217void help_init(void) 218{ 219 size_t allocsize = 0; /* Space needed for help_text. */ 220 const char *htx[3]; /* Untranslated help message. We break 221 * it up into three chunks in case the 222 * full string is too long for the 223 * compiler to handle. */ 224 char *ptr; 225 const shortcut *s; 226#ifndef NANO_TINY 227 const toggle *t; 228#ifdef ENABLE_NANORC 229 bool old_whitespace = ISSET(WHITESPACE_DISPLAY); 230 231 UNSET(WHITESPACE_DISPLAY); 232#endif 233#endif 234 235 /* First, set up the initial help text for the current function. */ 236 if (currshortcut == whereis_list || currshortcut == replace_list || 237 currshortcut == replace_list_2) { 238 htx[0] = N_("Search Command Help Text\n\n " 239 "Enter the words or characters you would like to " 240 "search for, and then press Enter. If there is a " 241 "match for the text you entered, the screen will be " 242 "updated to the location of the nearest match for the " 243 "search string.\n\n The previous search string will be " 244 "shown in brackets after the search prompt. Hitting " 245 "Enter without entering any text will perform the " 246 "previous search. "); 247 htx[1] = N_("If you have selected text with the mark and then " 248 "search to replace, only matches in the selected text " 249 "will be replaced.\n\n The following function keys are " 250 "available in Search mode:\n\n"); 251 htx[2] = NULL; 252 } else if (currshortcut == gotoline_list) { 253 htx[0] = N_("Go To Line Help Text\n\n " 254 "Enter the line number that you wish to go to and hit " 255 "Enter. If there are fewer lines of text than the " 256 "number you entered, you will be brought to the last " 257 "line of the file.\n\n The following function keys are " 258 "available in Go To Line mode:\n\n"); 259 htx[1] = NULL; 260 htx[2] = NULL; 261 } else if (currshortcut == insertfile_list) { 262 htx[0] = N_("Insert File Help Text\n\n " 263 "Type in the name of a file to be inserted into the " 264 "current file buffer at the current cursor " 265 "location.\n\n If you have compiled nano with multiple " 266 "file buffer support, and enable multiple file buffers " 267 "with the -F or --multibuffer command line flags, the " 268 "Meta-F toggle, or a nanorc file, inserting a file " 269 "will cause it to be loaded into a separate buffer " 270 "(use Meta-< and > to switch between file buffers). "); 271 htx[1] = N_("If you need another blank buffer, do not enter " 272 "any filename, or type in a nonexistent filename at " 273 "the prompt and press Enter.\n\n The following " 274 "function keys are available in Insert File mode:\n\n"); 275 htx[2] = NULL; 276 } else if (currshortcut == writefile_list) { 277 htx[0] = N_("Write File Help Text\n\n " 278 "Type the name that you wish to save the current file " 279 "as and press Enter to save the file.\n\n If you have " 280 "selected text with the mark, you will be prompted to " 281 "save only the selected portion to a separate file. To " 282 "reduce the chance of overwriting the current file with " 283 "just a portion of it, the current filename is not the " 284 "default in this mode.\n\n The following function keys " 285 "are available in Write File mode:\n\n"); 286 htx[1] = NULL; 287 htx[2] = NULL; 288 } 289#ifndef DISABLE_BROWSER 290 else if (currshortcut == browser_list) { 291 htx[0] = N_("File Browser Help Text\n\n " 292 "The file browser is used to visually browse the " 293 "directory structure to select a file for reading " 294 "or writing. You may use the arrow keys or Page Up/" 295 "Down to browse through the files, and S or Enter to " 296 "choose the selected file or enter the selected " 297 "directory. To move up one level, select the " 298 "directory called \"..\" at the top of the file " 299 "list.\n\n The following function keys are available " 300 "in the file browser:\n\n"); 301 htx[1] = NULL; 302 htx[2] = NULL; 303 } else if (currshortcut == whereis_file_list) { 304 htx[0] = N_("Browser Search Command Help Text\n\n " 305 "Enter the words or characters you would like to " 306 "search for, and then press Enter. If there is a " 307 "match for the text you entered, the screen will be " 308 "updated to the location of the nearest match for the " 309 "search string.\n\n The previous search string will be " 310 "shown in brackets after the search prompt. Hitting " 311 "Enter without entering any text will perform the " 312 "previous search.\n\n"); 313 htx[1] = N_(" The following function keys are available in " 314 "Browser Search mode:\n\n"); 315 htx[2] = NULL; 316 } else if (currshortcut == gotodir_list) { 317 htx[0] = N_("Browser Go To Directory Help Text\n\n " 318 "Enter the name of the directory you would like to " 319 "browse to.\n\n If tab completion has not been " 320 "disabled, you can use the Tab key to (attempt to) " 321 "automatically complete the directory name.\n\n The " 322 "following function keys are available in Browser Go " 323 "To Directory mode:\n\n"); 324 htx[1] = NULL; 325 htx[2] = NULL; 326 } 327#endif /* !DISABLE_BROWSER */ 328#ifndef DISABLE_SPELLER 329 else if (currshortcut == spell_list) { 330 htx[0] = N_("Spell Check Help Text\n\n " 331 "The spell checker checks the spelling of all text in " 332 "the current file. When an unknown word is " 333 "encountered, it is highlighted and a replacement can " 334 "be edited. It will then prompt to replace every " 335 "instance of the given misspelled word in the current " 336 "file, or, if you have selected text with the mark, in " 337 "the selected text.\n\n The following function keys " 338 "are available in Spell Check mode:\n\n"); 339 htx[1] = NULL; 340 htx[2] = NULL; 341 } 342#endif /* !DISABLE_SPELLER */ 343#ifndef NANO_TINY 344 else if (currshortcut == extcmd_list) { 345 htx[0] = N_("Execute Command Help Text\n\n " 346 "This mode allows you to insert the output of a " 347 "command run by the shell into the current buffer (or " 348 "a new buffer in multiple file buffer mode). If you " 349 "need another blank buffer, do not enter any " 350 "command.\n\n The following function keys are " 351 "available in Execute Command mode:\n\n"); 352 htx[1] = NULL; 353 htx[2] = NULL; 354 } 355#endif /* !NANO_TINY */ 356 else { 357 /* Default to the main help list. */ 358 htx[0] = N_("Main nano help text\n\n " 359 "The nano editor is designed to emulate the " 360 "functionality and ease-of-use of the UW Pico text " 361 "editor. There are four main sections of the editor. " 362 "The top line shows the program version, the current " 363 "filename being edited, and whether or not the file " 364 "has been modified. Next is the main editor window " 365 "showing the file being edited. The status line is " 366 "the third line from the bottom and shows important " 367 "messages. "); 368 htx[1] = N_("The bottom two lines show the most commonly used " 369 "shortcuts in the editor.\n\n The notation for " 370 "shortcuts is as follows: Control-key sequences are " 371 "notated with a caret (^) symbol and can be entered " 372 "either by using the Control (Ctrl) key or pressing " 373 "the Escape (Esc) key twice. Escape-key sequences are " 374 "notated with the Meta (M-) symbol and can be entered " 375 "using either the Esc, Alt, or Meta key depending on " 376 "your keyboard setup. "); 377 htx[2] = N_("Also, pressing Esc twice and then typing a " 378 "three-digit decimal number from 000 to 255 will enter " 379 "the character with the corresponding value. The " 380 "following keystrokes are available in the main editor " 381 "window. Alternative keys are shown in " 382 "parentheses:\n\n"); 383 } 384 385 htx[0] = _(htx[0]); 386 if (htx[1] != NULL) 387 htx[1] = _(htx[1]); 388 if (htx[2] != NULL) 389 htx[2] = _(htx[2]); 390 391 allocsize += strlen(htx[0]); 392 if (htx[1] != NULL) 393 allocsize += strlen(htx[1]); 394 if (htx[2] != NULL) 395 allocsize += strlen(htx[2]); 396 397 /* Count the shortcut help text. Each entry has up to three keys, 398 * which fill 24 columns, plus translated text, plus one or two 399 * \n's. */ 400 for (s = currshortcut; s != NULL; s = s->next) 401 allocsize += (24 * mb_cur_max()) + strlen(s->help) + 2; 402 403#ifndef NANO_TINY 404 /* If we're on the main list, we also count the toggle help text. 405 * Each entry has "M-%c\t\t\t", which fills 24 columns, plus a 406 * space, plus translated text, plus one or two '\n's. */ 407 if (currshortcut == main_list) { 408 size_t endis_len = strlen(_("enable/disable")); 409 410 for (t = toggles; t != NULL; t = t->next) 411 allocsize += strlen(t->desc) + endis_len + 9; 412 } 413#endif 414 415 /* help_text has been freed and set to NULL unless the user resized 416 * while in the help screen. */ 417 if (help_text != NULL) 418 free(help_text); 419 420 /* Allocate space for the help text. */ 421 help_text = charalloc(allocsize + 1); 422 423 /* Now add the text we want. */ 424 strcpy(help_text, htx[0]); 425 if (htx[1] != NULL) 426 strcat(help_text, htx[1]); 427 if (htx[2] != NULL) 428 strcat(help_text, htx[2]); 429 430 ptr = help_text + strlen(help_text); 431 432 /* Now add our shortcut info. Assume that each shortcut has, at the 433 * very least, an equivalent control key, an equivalent primary meta 434 * key sequence, or both. Also assume that the meta key values are 435 * not control characters. We can display a maximum of three 436 * shortcut entries. */ 437 for (s = currshortcut; s != NULL; s = s->next) { 438 int entries = 0; 439 440 /* Control key. */ 441 if (s->ctrlval != NANO_NO_KEY) { 442 entries++; 443 /* Yucky sentinel values that we can't handle a better 444 * way. */ 445 if (s->ctrlval == NANO_CONTROL_SPACE) { 446 char *space_ptr = display_string(_("Space"), 0, 14, 447 FALSE); 448 449 if (s->funcval == NANO_NO_KEY && (s->metaval == 450 NANO_NO_KEY || s->miscval == NANO_NO_KEY)) { 451 /* If we're here, we have at least two entries worth 452 * of blank space. If this entry takes up more than 453 * one entry's worth of space, use two to display 454 * it. */ 455 if (mbstrlen(space_ptr) > 6) 456 entries++; 457 } else 458 /* Otherwise, truncate it so that it takes up only 459 * one entry's worth of space. */ 460 space_ptr[6] = '\0'; 461 462 ptr += sprintf(ptr, "^%s", space_ptr); 463 464 free(space_ptr); 465 } else if (s->ctrlval == NANO_CONTROL_8) 466 ptr += sprintf(ptr, "^?"); 467 /* Normal values. */ 468 else 469 ptr += sprintf(ptr, "^%c", s->ctrlval + 64); 470 *(ptr++) = '\t'; 471 } 472 473 /* Function key. */ 474 if (s->funcval != NANO_NO_KEY) { 475 entries++; 476 /* If this is the first entry, put it in the middle. */ 477 if (entries == 1) { 478 entries++; 479 *(ptr++) = '\t'; 480 } 481 ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0); 482 *(ptr++) = '\t'; 483 } 484 485 /* Primary meta key sequence. If it's the first entry, don't 486 * put parentheses around it. */ 487 if (s->metaval != NANO_NO_KEY) { 488 entries++; 489 /* If this is the last entry, put it at the end. */ 490 if (entries == 2 && s->miscval == NANO_NO_KEY) { 491 entries++; 492 *(ptr++) = '\t'; 493 } 494 /* Yucky sentinel values that we can't handle a better 495 * way. */ 496 if (s->metaval == NANO_META_SPACE && entries == 1) { 497 char *space_ptr = display_string(_("Space"), 0, 13, 498 FALSE); 499 500 /* If we're here, we have at least two entries worth of 501 * blank space. If this entry takes up more than one 502 * entry's worth of space, use two to display it. */ 503 if (mbstrlen(space_ptr) > 5) 504 entries++; 505 506 ptr += sprintf(ptr, "M-%s", space_ptr); 507 508 free(space_ptr); 509 } else 510 /* Normal values. */ 511 ptr += sprintf(ptr, (entries == 1) ? "M-%c" : "(M-%c)", 512 toupper(s->metaval)); 513 *(ptr++) = '\t'; 514 } 515 516 /* Miscellaneous meta key sequence. */ 517 if (entries < 3 && s->miscval != NANO_NO_KEY) { 518 entries++; 519 /* If this is the last entry, put it at the end. */ 520 if (entries == 2) { 521 entries++; 522 *(ptr++) = '\t'; 523 } 524 ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval)); 525 *(ptr++) = '\t'; 526 } 527 528 /* If this entry isn't blank, make sure all the help text starts 529 * at the same place. */ 530 if (s->ctrlval != NANO_NO_KEY || s->funcval != NANO_NO_KEY || 531 s->metaval != NANO_NO_KEY || s->miscval != 532 NANO_NO_KEY) { 533 while (entries < 3) { 534 entries++; 535 *(ptr++) = '\t'; 536 } 537 } 538 539 /* The shortcut's help text. */ 540 ptr += sprintf(ptr, "%s\n", s->help); 541 542 if (s->blank_after) 543 ptr += sprintf(ptr, "\n"); 544 } 545 546#ifndef NANO_TINY 547 /* And the toggles... */ 548 if (currshortcut == main_list) { 549 for (t = toggles; t != NULL; t = t->next) { 550 ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", 551 toupper(t->val), t->desc, _("enable/disable")); 552 553 if (t->blank_after) 554 ptr += sprintf(ptr, "\n"); 555 } 556 } 557 558#ifdef ENABLE_NANORC 559 if (old_whitespace) 560 SET(WHITESPACE_DISPLAY); 561#endif 562#endif 563 564 /* If all went well, we didn't overwrite the allocated space for 565 * help_text. */ 566 assert(strlen(help_text) <= allocsize + 1); 567} 568 569/* Determine the shortcut key corresponding to the values of kbinput 570 * (the key itself), meta_key (whether the key is a meta sequence), and 571 * func_key (whether the key is a function key), if any. In the 572 * process, convert certain non-shortcut keys into their corresponding 573 * shortcut keys. */ 574void parse_help_input(int *kbinput, bool *meta_key, bool *func_key) 575{ 576 get_shortcut(help_list, kbinput, meta_key, func_key); 577 578 if (!*meta_key) { 579 switch (*kbinput) { 580 /* For consistency with the file browser. */ 581 case ' ': 582 *kbinput = NANO_NEXTPAGE_KEY; 583 break; 584 case '-': 585 *kbinput = NANO_PREVPAGE_KEY; 586 break; 587 /* Cancel is equivalent to Exit here. */ 588 case NANO_CANCEL_KEY: 589 case 'E': 590 case 'e': 591 *kbinput = NANO_EXIT_KEY; 592 break; 593 } 594 } 595} 596 597/* Calculate the next line of help_text, starting at ptr. */ 598size_t help_line_len(const char *ptr) 599{ 600 int help_cols = (COLS > 24) ? COLS - 1 : 24; 601 602 /* Try to break the line at (COLS - 1) columns if we have more than 603 * 24 columns, and at 24 columns otherwise. */ 604 ssize_t wrap_loc = break_line(ptr, help_cols, TRUE); 605 size_t retval = (wrap_loc < 0) ? 0 : wrap_loc; 606 size_t retval_save = retval; 607 608 /* Get the length of the entire line up to a null or a newline. */ 609 while (*(ptr + retval) != '\0' && *(ptr + retval) != '\n') 610 retval += move_mbright(ptr + retval, 0); 611 612 /* If the entire line doesn't go more than one column beyond where 613 * we tried to break it, we should display it as-is. Otherwise, we 614 * should display it only up to the break. */ 615 if (strnlenpt(ptr, retval) > help_cols + 1) 616 retval = retval_save; 617 618 return retval; 619} 620 621#endif /* !DISABLE_HELP */ 622