1 /*
2  * Copyright (c) 2002-2012 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * Keyboard input processing for AG_Editable(3).
28  */
29 
30 #include <agar/core/core.h>
31 #include <agar/gui/widget.h>
32 #include <agar/gui/window.h>
33 #include <agar/gui/editable.h>
34 #include <agar/gui/keymap.h>
35 #include <agar/gui/text.h>
36 #include <agar/gui/gui_math.h>
37 
38 #include <ctype.h>
39 #include <string.h>
40 
41 /* Insert a new character at current cursor position. */
42 static int
Insert(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 ch)43 Insert(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 ch)
44 {
45 	Uint32 ins[3];
46 	int i, nIns;
47 	Uint32 uch = ch;
48 
49 	if (keysym == 0)
50 		return (0);
51 #ifdef __APPLE__
52 	if ((keymod & AG_KEYMOD_LMETA) ||
53 	    (keymod & AG_KEYMOD_RMETA))
54 		return (0);
55 #endif
56 
57 	if (!(ed->flags & AG_EDITABLE_NOLATIN1)) {
58 		for (i = 0; ; i++) {
59 			const struct ag_key_mapping *km = &agKeymapLATIN1[i];
60 
61 			if (keysym == km->key) {
62 				if (((keymod & AG_KEYMOD_ALT) &&
63 				     (keymod & AG_KEYMOD_SHIFT) &&
64 				     (km->modmask == (AG_KEYMOD_ALT|AG_KEYMOD_SHIFT)))) {
65 					uch = km->unicode;
66 					break;
67 				} else if (keymod & AG_KEYMOD_ALT &&
68 				    km->modmask == AG_KEYMOD_ALT) {
69 					uch = km->unicode;
70 					break;
71 				}
72 			} else if (km->key == AG_KEY_LAST) {
73 				break;
74 			}
75 		}
76 	}
77 
78 	if (uch == 0) { return (0); }
79 	if (uch == '\r') { uch = '\n'; }
80 
81 	if (Strcasecmp(ed->encoding, "US-ASCII") == 0 &&
82 	    !isascii((int)uch))
83 		return (0);
84 
85 	if (agTextComposition) {
86 		if ((nIns = AG_KeyInputCompose(ed, uch, ins)) == 0)
87 			return (0);
88 	} else {
89 		ins[0] = uch;
90 		nIns = 1;
91 	}
92 	ins[nIns] = '\0';
93 
94 	if (ed->sel != 0) {
95 		AG_EditableDelete(ed, buf);
96 	}
97 	if (AG_EditableGrowBuffer(ed, buf, ins, (size_t)nIns) == -1) {
98 		Verbose("Insert Failed: %s\n", AG_GetError());
99 		return (0);
100 	}
101 
102 	if (ed->pos == buf->len) {				/* Append */
103 		for (i = 0; i < nIns; i++)
104 			buf->s[buf->len + i] = ins[i];
105 	} else {						/* Insert */
106 		memmove(&buf->s[ed->pos + nIns], &buf->s[ed->pos],
107 		       (buf->len - ed->pos)*sizeof(Uint32));
108 		for (i = 0; i < nIns; i++)
109 			buf->s[ed->pos + i] = ins[i];
110 	}
111 	buf->len += nIns;
112 	buf->s[buf->len] = '\0';
113 	ed->pos += nIns;
114 
115 	if (!(ed->flags & AG_EDITABLE_MULTILINE)) {	/* Optimize case */
116 		int wIns;
117 		AG_TextSizeUCS4(ins, &wIns, NULL);
118 		ed->xScrollPx += wIns;
119 	} else {
120 		ed->xScrollTo = &ed->xCurs;
121 		ed->yScrollTo = &ed->yCurs;
122 	}
123 	ed->flags |= AG_EDITABLE_BLINK_ON;
124 	return (1);
125 }
126 
127 /* Delete the character at cursor, or the active selection. */
128 static int
Delete(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 unicode)129 Delete(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 unicode)
130 {
131 	Uint32 *c;
132 	int wDel;
133 
134 	if (buf->len == 0)
135 		return (0);
136 
137 	if (ed->sel != 0) {
138 		AG_EditableDelete(ed, buf);
139 		return (1);
140 	}
141 	if (keysym == AG_KEY_BACKSPACE && ed->pos == 0) {
142 		return (0);
143 	}
144 	if (ed->pos == buf->len) {
145 		ed->pos--;
146 		buf->s[--buf->len] = '\0';
147 
148 		if (ed->flags & AG_EDITABLE_MULTILINE) {
149 			ed->xScrollTo = &ed->xCurs;
150 			ed->yScrollTo = &ed->yCurs;
151 		} else {
152 			AG_TextSizeUCS4(&buf->s[buf->len-1], &wDel, NULL);
153 			if (ed->x > 0) { ed->x -= wDel; }
154 		}
155 		return (1);
156 	}
157 	if (keysym == AG_KEY_BACKSPACE)
158 		ed->pos--;
159 
160 	if (ed->flags & AG_EDITABLE_MULTILINE) {
161 		ed->xScrollTo = &ed->xCurs;
162 		ed->yScrollTo = &ed->yCurs;
163 	} else {
164 		Uint32 cDel[2];
165 
166 		cDel[0] = buf->s[ed->pos];
167 		cDel[1] = '\0';
168 		AG_TextSizeUCS4(cDel, &wDel, NULL);
169 		if (ed->x > 0) { ed->x -= wDel; }
170 	}
171 
172 	for (c = &buf->s[ed->pos];
173 	     c < &buf->s[buf->len + 1];
174 	     c++) {
175 		*c = c[1];
176 		if (*c == '\0')
177 			break;
178 	}
179 	buf->len--;
180 	return (1);
181 }
182 
183 /* Copy the selection to clipboard. */
184 static int
Copy(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)185 Copy(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
186 {
187 	AG_EditableCopy(ed, buf, &agEditableClipbrd);
188 	return (0);
189 }
190 
191 /* Copy selection to clipboard and subsequently delete it. */
192 static int
Cut(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)193 Cut(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
194 {
195 	return AG_EditableCut(ed, buf, &agEditableClipbrd);
196 }
197 
198 /* Paste clipboard contents to current cursor position. */
199 static int
Paste(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)200 Paste(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
201 {
202 	return AG_EditablePaste(ed, buf, &agEditableClipbrd);
203 }
204 
205 /*
206  * Kill the current selection; if there is no selection, cut the
207  * characters up to the end of the line (Emacs-style).
208  */
209 static int
Kill(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)210 Kill(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
211 {
212 	Uint32 *c;
213 
214 	if (ed->sel != 0) {
215 		AG_EditableValidateSelection(ed, buf);
216 		if (ed->sel < 0) {
217 			ed->pos += ed->sel;
218 			ed->sel = -(ed->sel);
219 		}
220 	} else {
221 		for (c = &buf->s[ed->pos]; c < &buf->s[buf->len]; c++) {
222 			if (*c == '\n') {
223 				break;
224 			}
225 			ed->sel++;
226 		}
227 		if (ed->sel == 0)
228 			return (0);
229 	}
230 
231 	AG_EditableCopyChunk(ed, &agEditableKillring, &buf->s[ed->pos], ed->sel);
232 	AG_EditableDelete(ed, buf);
233 	return (1);
234 }
235 
236 /* Paste the contents of the Emacs-style kill ring at cursor position. */
237 static int
Yank(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)238 Yank(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
239 {
240 	return AG_EditablePaste(ed, buf, &agEditableKillring);
241 }
242 
243 /* Seek one word backwards. */
244 static int
WordBack(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)245 WordBack(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
246 {
247 	int newPos = ed->pos;
248 	Uint32 *c;
249 
250 	/* XXX: handle other types of spaces */
251 	if (ed->pos > 1 && buf->s[newPos-1] == ' ') {
252 		newPos -= 2;
253 	}
254 	for (c = &buf->s[newPos];
255 	     c > &buf->s[0] && *c != ' ';
256 	     c--, newPos--)
257 		;;
258 	if (*c == ' ')
259 		newPos++;
260 
261 	if (keymod & AG_KEYMOD_SHIFT) {
262 		ed->sel += (ed->pos - newPos);
263 	} else {
264 		ed->sel = 0;
265 	}
266 	ed->pos = newPos;
267 
268 	ed->flags |= AG_EDITABLE_MARKPREF;
269 	ed->flags |= AG_EDITABLE_BLINK_ON;
270 	ed->xScrollTo = &ed->xCurs;
271 	ed->yScrollTo = &ed->yCurs;
272 	AG_Redraw(ed);
273 	return (0);
274 }
275 
276 /* Seek one word forward. */
277 static int
WordForw(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)278 WordForw(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
279 {
280 	int newPos = ed->pos;
281 	Uint32 *c;
282 
283 	if (newPos == buf->len) {
284 		return (0);
285 	}
286 	if (buf->len > 1 && buf->s[newPos] == ' ') {
287 		newPos++;
288 	}
289 	for (c = &buf->s[newPos];
290 	     *c != '\0' && *c != ' ';
291 	     c++, newPos++)
292 		;;
293 
294 	if (keymod & AG_KEYMOD_SHIFT) {
295 		ed->sel += (ed->pos - newPos);
296 	} else {
297 		ed->sel = 0;
298 	}
299 	ed->pos = newPos;
300 
301 	ed->flags |= AG_EDITABLE_MARKPREF;
302 	ed->flags |= AG_EDITABLE_BLINK_ON;
303 	ed->xScrollTo = &ed->xCurs;
304 	ed->yScrollTo = &ed->yCurs;
305 	AG_Redraw(ed);
306 	return (0);
307 }
308 
309 /* Select all. */
310 static int
SelectAll(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)311 SelectAll(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
312 {
313 	AG_EditableSelectAll(ed, buf);
314 	return (0);
315 }
316 
317 /* Move cursor to beginning of line. */
318 static int
CursorHome(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)319 CursorHome(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
320 {
321 	int newPos = ed->pos;
322 	Uint32 *c;
323 
324 	if (ed->flags & AG_EDITABLE_MULTILINE) {
325 		if (newPos == 0) {
326 			return (0);
327 		}
328 		for (c = &buf->s[newPos - 1];
329 		     c >= &buf->s[0] && newPos >= 0;
330 		     c--, newPos--) {
331 			if (*c == '\n')
332 				break;
333 		}
334 	} else {
335 		newPos = 0;
336 	}
337 
338 	if (keymod & AG_KEYMOD_SHIFT) {
339 		ed->sel += (ed->pos - newPos);
340 	} else {
341 		ed->sel = 0;
342 	}
343 	ed->pos = newPos;
344 
345 	ed->x = 0;
346 	ed->flags |= AG_EDITABLE_MARKPREF;
347 	AG_Redraw(ed);
348 	return (0);
349 }
350 
351 /* Move cursor to end of line. */
352 static int
CursorEnd(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)353 CursorEnd(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
354 {
355 	int newPos = ed->pos;
356 	Uint32 *c;
357 
358 	if (ed->flags & AG_EDITABLE_MULTILINE) {
359 		if (newPos == buf->len || buf->s[newPos] == '\n') {
360 			return (0);
361 		}
362 		for (c = &buf->s[newPos + 1];
363 		     c <= &buf->s[buf->len] && newPos <= buf->len;
364 		     c++, newPos++) {
365 			if (*c == '\n') {
366 				newPos++;
367 				break;
368 			}
369 		}
370 		if (newPos > buf->len)
371 			newPos = buf->len;
372 	} else {
373 		newPos = buf->len;
374 	}
375 
376 	if (keymod & AG_KEYMOD_SHIFT) {
377 		ed->sel += (ed->pos - newPos);
378 	} else {
379 		ed->sel = 0;
380 	}
381 	ed->pos = newPos;
382 
383 	ed->flags |= AG_EDITABLE_MARKPREF;
384 	ed->xScrollTo = &ed->xCurs;
385 	ed->yScrollTo = &ed->yCurs;
386 	AG_Redraw(ed);
387 	return (0);
388 }
389 
390 /* Move cursor left. */
391 static int
CursorLeft(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)392 CursorLeft(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
393 {
394 	if ((ed->pos - 1) >= 0) {
395 		ed->pos--;
396 		if (keymod & AG_KEYMOD_SHIFT) {
397 			ed->sel++;
398 		} else {
399 			ed->sel = 0;
400 		}
401 	} else {
402 		ed->pos = 0;
403 	}
404 	ed->flags |= AG_EDITABLE_MARKPREF;
405 	ed->flags |= AG_EDITABLE_BLINK_ON;
406 	ed->xScrollTo = &ed->xCurs;
407 	ed->yScrollTo = &ed->yCurs;
408 	AG_Redraw(ed);
409 	return (0);
410 }
411 
412 /* Move cursor right. */
413 static int
CursorRight(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)414 CursorRight(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
415 {
416 	if (ed->pos < buf->len) {
417 		ed->pos++;
418 		if (keymod & AG_KEYMOD_SHIFT) {
419 			ed->sel--;
420 		} else {
421 			ed->sel = 0;
422 		}
423 	}
424 	ed->flags |= AG_EDITABLE_MARKPREF;
425 	ed->flags |= AG_EDITABLE_BLINK_ON;
426 	ed->xScrollTo = &ed->xCurs;
427 	ed->yScrollTo = &ed->yCurs;
428 	AG_Redraw(ed);
429 	return (0);
430 }
431 
432 /* Move cursor up in a multi-line string. */
433 static int
CursorUp(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)434 CursorUp(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
435 {
436 	int prevPos = ed->pos;
437 	int prevSel = ed->sel;
438 
439 	if (!(ed->flags & AG_EDITABLE_MULTILINE))
440 		return (0);
441 
442 	AG_EditableMoveCursor(ed, buf, ed->xCursPref,
443 	    (ed->yCurs - ed->y - 1)*agTextFontLineSkip + 1);
444 
445 	if (keymod & AG_KEYMOD_SHIFT) {
446 		ed->sel = prevSel - (ed->pos - prevPos);
447 	} else {
448 		ed->sel = 0;
449 	}
450 
451 	ed->flags |= AG_EDITABLE_BLINK_ON;
452 	ed->xScrollTo = &ed->xCurs;
453 	ed->yScrollTo = &ed->yCurs;
454 	AG_Redraw(ed);
455 	return (0);
456 }
457 
458 /* Move cursor down in a multi-line string. */
459 static int
CursorDown(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)460 CursorDown(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
461 {
462 	int prevPos = ed->pos;
463 	int prevSel = ed->sel;
464 
465 	if (!(ed->flags & AG_EDITABLE_MULTILINE))
466 		return (0);
467 
468 	AG_EditableMoveCursor(ed, buf, ed->xCursPref,
469 	    (ed->yCurs - ed->y + 1)*agTextFontLineSkip + 1);
470 
471 	if (keymod & AG_KEYMOD_SHIFT) {
472 		ed->sel = prevSel - (ed->pos - prevPos);
473 	} else {
474 		ed->sel = 0;
475 	}
476 
477 	ed->flags |= AG_EDITABLE_BLINK_ON;
478 	ed->xScrollTo = &ed->xCurs;
479 	ed->yScrollTo = &ed->yCurs;
480 	AG_Redraw(ed);
481 	return (0);
482 }
483 
484 /* Move cursor one page up in a multi-line string. */
485 static int
PageUp(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)486 PageUp(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
487 {
488 	int prevPos = ed->pos;
489 	int prevSel = ed->sel;
490 
491 	if (!(ed->flags & AG_EDITABLE_MULTILINE))
492 		return (0);
493 
494 	AG_EditableMoveCursor(ed, buf, ed->xCurs,
495 	    (ed->yCurs - ed->y - ed->yVis)*agTextFontLineSkip + 1);
496 
497 	if (keymod & AG_KEYMOD_SHIFT) {
498 		ed->sel = prevSel - (ed->pos - prevPos);
499 	} else {
500 		ed->sel = 0;
501 	}
502 
503 	ed->xScrollTo = &ed->xCurs;
504 	ed->yScrollTo = &ed->yCurs;
505 	AG_Redraw(ed);
506 	return (0);
507 }
508 
509 /* Move cursor one page down in a multi-line string. */
510 static int
PageDown(AG_Editable * ed,AG_EditableBuffer * buf,AG_KeySym keysym,Uint keymod,Uint32 uch)511 PageDown(AG_Editable *ed, AG_EditableBuffer *buf, AG_KeySym keysym, Uint keymod, Uint32 uch)
512 {
513 	int prevPos = ed->pos;
514 	int prevSel = ed->sel;
515 
516 	if (!(ed->flags & AG_EDITABLE_MULTILINE))
517 		return (0);
518 
519 	AG_EditableMoveCursor(ed, buf, ed->xCurs,
520 	    (ed->yCurs - ed->y + ed->yVis)*agTextFontLineSkip + 1);
521 
522 	if (keymod & AG_KEYMOD_SHIFT) {
523 		ed->sel = prevSel - (ed->pos - prevPos);
524 	} else {
525 		ed->sel = 0;
526 	}
527 
528 	ed->xScrollTo = &ed->xCurs;
529 	ed->yScrollTo = &ed->yCurs;
530 	AG_Redraw(ed);
531 	return (0);
532 }
533 
534 /*
535  * Map AG_KeySym(3) and AG_KeyMod(3) values to AG_Editable functions.
536  * Variants with modifier keys must appear first in the list. The
537  * modifiers string is passed to AG_CompareKeyMods(3).
538  *
539  * Available flags:
540  *	"w" = Require a writeable buffer
541  *	"e" = AG_EDITABLE_NOEMACS must be unset
542  */
543 const struct ag_keycode agKeymap[] = {
544 #ifdef __APPLE__
545 	{ AG_KEY_LEFT,		"CM",		CursorHome,	"" },
546 	{ AG_KEY_RIGHT,		"CM",		CursorEnd,	"" },
547 	{ AG_KEY_LEFT,		"A",		WordBack,	"" },
548 	{ AG_KEY_RIGHT,		"A",		WordForw,	"" },
549 	{ AG_KEY_A,		"M",		SelectAll,	"" },
550 	{ AG_KEY_C,		"M",		Copy,		"" },
551 	{ AG_KEY_X,		"M",		Cut,		"w" },
552 	{ AG_KEY_V,		"M",		Paste,		"w" },
553 	{ AG_KEY_K,		"M",		Kill,		"we" },
554 	{ AG_KEY_Y,		"M",		Yank,		"we" },
555 #else /* __APPLE__ */
556 	{ AG_KEY_LEFT,		"C",		WordBack,	"" },
557 	{ AG_KEY_RIGHT,		"C",		WordForw,	"" },
558 	{ AG_KEY_A,		"C",		SelectAll,	"" },
559 	{ AG_KEY_C,		"C",		Copy,		"" },
560 	{ AG_KEY_X,		"C",		Cut,		"w" },
561 	{ AG_KEY_V,		"C",		Paste,		"w" },
562 	{ AG_KEY_K,		"C",		Kill,		"we" },
563 	{ AG_KEY_Y,		"C",		Yank,		"we" },
564 #endif /* !__APPLE__ */
565 	{ AG_KEY_HOME,		"",		CursorHome,	"" },
566 	{ AG_KEY_END,		"",		CursorEnd,	"" },
567 	{ AG_KEY_LEFT,		"",		CursorLeft,	"" },
568 	{ AG_KEY_RIGHT,		"",		CursorRight,	"" },
569 	{ AG_KEY_UP,		"",		CursorUp,	"" },
570 	{ AG_KEY_DOWN,		"",		CursorDown,	"" },
571 	{ AG_KEY_PAGEUP,	"",		PageUp,		"" },
572 	{ AG_KEY_PAGEDOWN,	"",		PageDown,	"" },
573 	{ AG_KEY_BACKSPACE,	"",		Delete,		"w" },
574 	{ AG_KEY_DELETE,	"",		Delete,		"w" },
575 	{ AG_KEY_LAST,		"",		Insert,		"w" },
576 };
577