* For mailing lists, Alpine adds a description of the type of link
[alpine.git] / pico / bind.c
blob9804a0614dcc6480f370c8bc6eec02eb69afa5fc
1 /*
2 * ========================================================================
3 * Copyright 2006-2007 University of Washington
4 * Copyright 2013-2022 Eduardo Chappa
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
14 * Program: Key binding routines
17 /* This file is for functions having to do with key bindings,
18 descriptions, help commands, and command line execution.
20 written 11-feb-86 by Daniel Lawrence
23 #include "headers.h"
25 int arraylen(char **);
27 /*
28 * help - help function for pico (UW pared down version of uemacs).
29 * this function will intentionally garbage with helpful
30 * tips and then wait for a ' ' to be hit to then update the
31 * screen.
35 static char *helptext[] = {
36 /* TRANSLATORS: The next several lines go together as help text.
37 Leave the ~ characters where they are, they cause the following
38 character to be printed in boldface as long as the first character
39 of the line is also a ~. */
40 N_(" Pico Help Text"),
41 " ",
42 N_(" Pico is designed to be a simple, easy-to-use text editor with a"),
43 N_(" layout very similar to the Alpine mailer. The status line at the"),
44 N_(" top of the display shows pico's version, the current file being"),
45 N_(" edited and whether or not there are outstanding modifications"),
46 N_(" that have not been saved. The third line from the bottom is used"),
47 N_(" to report informational messages and for additional command input."),
48 N_(" The bottom two lines list the available editing commands."),
49 " ",
50 N_(" Each character typed is automatically inserted into the buffer"),
51 N_(" at the current cursor position. Editing commands and cursor"),
52 N_(" movement (besides arrow keys) are given to pico by typing"),
53 N_(" special control-key sequences. A caret, '^', is used to denote"),
54 N_("~ the control key, sometimes marked \"CTRL\", so the ~C~T~R~L~-~q key"),
55 N_("~ combination is written as ~^~Q."),
56 " ",
57 N_(" The following functions are available in pico (where applicable,"),
58 N_(" corresponding function key commands are in parentheses)."),
59 " ",
60 N_("~ ~^~G (~F~1) Display this help text."),
61 " ",
62 N_("~ ~^~F move Forward a character."),
63 N_("~ ~^~B move Backward a character."),
64 N_("~ ~^~P move to the Previous line."),
65 N_("~ ~^~N move to the Next line."),
66 N_("~ ~^~A move to the beginning of the current line."),
67 N_("~ ~^~E move to the End of the current line."),
68 N_("~ ~^~V (~F~8) move forward a page of text."),
69 N_("~ ~^~Y (~F~7) move backward a page of text."),
70 " ",
71 N_("~ ~^~W (~F~6) Search for (where is) text, neglecting case."),
72 N_("~ ~^~L Refresh the display."),
73 " ",
74 N_("~ ~^~D Delete the character at the cursor position."),
75 N_("~ ~^~^ Mark cursor position as beginning of selected text."),
76 N_(" Note: Setting mark when already set unselects text."),
77 N_("~ ~^~K (~F~9) Cut selected text (displayed in inverse characters)."),
78 N_(" Note: The selected text's boundary on the cursor side"),
79 N_(" ends at the left edge of the cursor. So, with "),
80 N_(" selected text to the left of the cursor, the "),
81 N_(" character under the cursor is not selected."),
82 N_("~ ~^~U (~F~1~0) Uncut (paste) last cut text inserting it at the"),
83 N_(" current cursor position."),
84 N_("~ ~^~I Insert a tab at the current cursor position."),
85 " ",
86 N_("~ ~^~J (~F~4) Format (justify) the current paragraph."),
87 N_(" Note: paragraphs delimited by blank lines or indentation."),
88 N_("~ ~^~T (~F~1~2) To invoke the spelling checker"),
89 N_("~ ~^~C (~F~1~1) Report current cursor position"),
90 " ",
91 N_("~ ~^~R (~F~5) Insert an external file at the current cursor position."),
92 N_("~ ~^~O (~F~3) Output the current buffer to a file, saving it."),
93 N_("~ ~^~X (~F~2) Exit pico, saving buffer."),
94 " ",
95 N_(" End of Help."),
96 " ",
97 NULL
102 * arraylen - return the number of bytes in an array of char
105 arraylen(char **array)
107 register int i=0;
109 while(array[i++] != NULL) ;
110 return(i);
115 * whelp - display help text for the composer and pico
118 whelp(int f, int n)
120 if(term.t_mrow == 0){ /* blank keymenu in effect */
121 if(km_popped == 0){
122 /* cause keymenu display */
123 km_popped = 2;
124 if(!Pmaster)
125 sgarbf = TRUE;
127 return(TRUE);
131 if(Pmaster){
132 VARS_TO_SAVE *saved_state;
134 saved_state = save_pico_state();
135 (*Pmaster->helper)(Pmaster->composer_help,
136 Pmaster->headents
137 ? _("Help for the Alpine Composer")
138 : _("Help for Signature Editor"),
140 if(saved_state){
141 restore_pico_state(saved_state);
142 free_pico_state(saved_state);
145 ttresize();
146 picosigs(); /* restore any altered handlers */
147 curwp->w_flag |= WFMODE;
148 if(km_popped) /* this will unpop us */
149 curwp->w_flag |= WFHARD;
151 else{
152 int mrow_was_zero = 0;
154 /* always want keyhelp showing during help */
155 if(term.t_mrow == 0){
156 mrow_was_zero++;
157 term.t_mrow = 2;
160 /* TRANSLATORS: Pico is the name of a program */
161 pico_help(helptext, _("Help for Pico"), 1);
162 /* put it back the way it was */
163 if(mrow_was_zero)
164 term.t_mrow = 0;
167 sgarbf = TRUE;
168 return(FALSE);
171 static KEYMENU menu_scroll[] = {
172 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
173 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
174 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
175 {"^X", N_("Exit Help"), KS_NONE}, {NULL, NULL, KS_NONE},
176 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE},
177 {NULL, NULL, KS_NONE}, {NULL, NULL, KS_NONE}
179 #define PREV_KEY 3
180 #define NEXT_KEY 9
183 #define OVERLAP 2 /* displayed page overlap */
186 * scrollw - takes beginning row and ending row to display an array
187 * of text lines. returns either 0 if scrolling terminated
188 * normally or the value of a ctrl character typed to end it.
190 * updates -
191 * 01/11/89 - added stripe call if 1st char is tilde - '~'
195 wscrollw(int begrow, int endrow, char *utf8textp[], int textlen)
197 register int loffset = 0;
198 register int prevoffset = -1;
199 register int dlines;
200 register int i;
201 register int cont;
202 register int done = 0;
203 register char *buf;
204 UCS c;
206 dlines = endrow - begrow - 1;
207 while(!done) {
209 * display a page loop ...
211 if(prevoffset != loffset){
212 for(i = 0; i < dlines; i++){
213 movecursor(i + begrow, 0);
214 peeol();
215 if((loffset+i) < textlen){
216 buf = _(&(utf8textp[loffset+i][0]));
217 if(*buf == '~'){
218 buf++;
219 wstripe(begrow+i, 0, buf, '~');
221 else{
222 pputs_utf8(buf, 0);
227 * put up the options prompt
229 movecursor(begrow + dlines, 0);
230 cont = (loffset+dlines < textlen);
231 if(cont){ /* continue ? */
232 menu_scroll[NEXT_KEY].name = "^V";
233 /* TRANSLATORS: Next Page, a command key label */
234 menu_scroll[NEXT_KEY].label = N_("Next Pg");
236 else
237 menu_scroll[NEXT_KEY].name = NULL;
239 if(loffset){
240 menu_scroll[PREV_KEY].name = "^Y";
241 /* TRANSLATORS: Previous Page */
242 menu_scroll[PREV_KEY].label = N_("Prev Pg");
244 else
245 menu_scroll[PREV_KEY].name = NULL;
247 wkeyhelp(menu_scroll);
250 (*term.t_flush)();
252 c = GetKey();
254 prevoffset = loffset;
255 switch(c){
256 case (CTRL|'X') : /* quit */
257 case F2 :
258 done = 1;
259 break;
260 case (CTRL|'Y') : /* prev page */
261 case F7 : /* prev page */
262 if((loffset-dlines-OVERLAP) > 0){
263 loffset -= (dlines-OVERLAP);
265 else{
266 if(loffset != 0){
267 prevoffset = -1;
269 else{
270 (*term.t_beep)();
272 loffset = 0;
274 break;
275 case (CTRL|'V') : /* next page */
276 case F8 :
277 if(cont){
278 loffset += (dlines-OVERLAP);
280 else{
281 (*term.t_beep)();
283 break;
284 case '\016' : /* prev-line */
285 case (CTRL|'N') :
286 if(cont)
287 loffset++;
288 else
289 (*term.t_beep)();
290 break;
291 case '\020' : /* prev-line */
292 case (CTRL|'P') :
293 if(loffset > 0)
294 loffset--;
295 else
296 (*term.t_beep)();
297 break;
298 case '\014' : /* refresh */
299 case (CTRL|'L') : /* refresh */
300 modeline(curwp);
301 update();
302 prevoffset = -1;
303 break;
304 case NODATA :
305 break;
306 #ifdef notdef
308 * We don't handle window resize events correctly when in pico help.
309 * resize_pico() redraws the edit window instead of the help window.
310 * A ^L will redraw the help text. What we'd like is something like
311 * a KEY_RESIZE return from GetKey. If we had that we could exit
312 * wscrollw with a FALSE return value and have that cause us to loop
313 * back into wscrollw with the adjusted size. That would still mean
314 * the edit text would be redrawn first...
316 #endif /* notdef */
317 default :
318 unknown_command(c);
319 break;
323 return(TRUE);
328 * normalize_cmd - given a char and list of function key to command key
329 * mappings, return, depending on gmode, the right command.
330 * The list is an array of (Fkey, command-key) pairs.
331 * sc is the index in the array that means to ignore fkey
332 * vs. command key mapping
334 * rules: 1. if c not in table (either fkey or command), let it thru
335 * 2. if c matches, but in other mode, punt it
338 normalize_cmd(UCS c, UCS list[][2], int sc)
340 int i;
342 for(i=0; i < 12; i++){
343 if(c == list[i][(FUNC&c) ? 0 : 1]){ /* in table? */
344 if(i == sc) /* SPECIAL CASE! */
345 return(list[i][1]);
347 if(list[i][1] == NODATA) /* no mapping ! */
348 return(c);
350 if(((FUNC&c) == FUNC) && !((gmode&MDFKEY) == MDFKEY))
351 return(c); /* not allowed, let caller handle it */
352 else
353 return(list[i][1]); /* never return func keys */
357 return(c);
362 * rebind - replace the first function with the second
364 void
365 rebindfunc(int (*a)(int, int), int (*b)(int, int))
367 KEYTAB *kp;
369 kp = (Pmaster) ? &keytab[0] : &pkeytab[0];
371 while(kp->k_fp != NULL){ /* go thru whole list, and */
372 if(kp->k_fp == a)
373 kp->k_fp = b; /* replace all occurrences */
374 kp++;
380 * bindtokey - bind function f to command c
383 bindtokey(UCS c, int (*f)(int, int))
385 KEYTAB *kp, *ktab = (Pmaster) ? &keytab[0] : &pkeytab[0];
387 for(kp = ktab; kp->k_fp; kp++)
388 if(kp->k_code == c){
389 kp->k_fp = f; /* set to new function */
390 break;
393 /* not found? create new binding */
394 if(!kp->k_fp && kp < &ktab[NBINDS]){
395 kp->k_code = c; /* assign new code and function */
396 kp->k_fp = f;
397 (++kp)->k_code = 0; /* and null out next slot */
398 kp->k_fp = NULL;
401 return(TRUE);