/*
* Copyright (c) 2002-2008 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Basic keyboard input processing in UTF-8 mode.
*/
#include
#ifdef UTF8
#include
#include
#include "widget.h"
#include "editable.h"
#include "keymap.h"
#include "text.h"
#include
#include
#ifdef AG_THREADS
static AG_Mutex killRingLock = AG_MUTEX_INITIALIZER;
#endif
static Uint32 *killRing = NULL;
static size_t killRingLen = 0;
static int
InsertUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 ch,
Uint32 *ucs, int len, int bufSize)
{
int unicodeKbd = SDL_EnableUNICODE(-1);
Uint32 ins[3];
int i, nins;
Uint32 uch = ch;
if (AG_WidgetDisabled(ed) || keysym == 0)
return (0);
if (!unicodeKbd) {
uch = AG_ApplyModifiersASCII((Uint32)keysym, keymod);
}
if (!(ed->flags & AG_EDITABLE_NOLATIN1)) {
for (i = 0; ; i++) {
const struct ag_key_mapping *km = &agKeymapLATIN1[i];
if (keysym == km->key) {
if (((keymod & KMOD_ALT) &&
(keymod & KMOD_SHIFT) &&
(km->modmask == (KMOD_ALT|KMOD_SHIFT)))) {
uch = km->unicode;
break;
} else if (keymod & KMOD_ALT &&
km->modmask == KMOD_ALT) {
uch = km->unicode;
break;
}
} else if (km->key == SDLK_LAST) {
break;
}
}
}
if (uch == 0) { return (0); }
if (uch == '\r') { uch = '\n'; }
switch (ed->encoding) {
case AG_ENCODING_UTF8:
break;
case AG_ENCODING_ASCII:
if (!isascii((int)uch)) {
return (0);
}
break;
}
if (AG_GetBool(agConfig,"input.composition")) {
if ((nins = AG_KeyInputCompose(ed, uch, ins)) == 0)
return (0);
} else {
ins[0] = uch;
nins = 1;
}
ins[nins] = '\0';
/* We need the expanded UTF-8 length to check bounds. */
/* XXX optimize for STATIC */
if (AG_LengthUTF8FromUCS4(ucs) +
AG_LengthUTF8FromUCS4(ins) + 1 >= bufSize)
return (0);
if (ed->pos == len) { /* Append */
for (i = 0; i < nins; i++)
ucs[len+i] = ins[i];
} else { /* Insert */
memmove(&ucs[ed->pos+nins], &ucs[ed->pos],
(len - ed->pos)*sizeof(Uint32));
for (i = 0; i < nins; i++)
ucs[ed->pos+i] = ins[i];
}
ucs[len+nins] = '\0';
ed->pos += nins;
return (1);
}
static int
DeleteUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 unicode,
Uint32 *ucs, int len, int bufSize)
{
Uint32 *c;
if (AG_WidgetDisabled(ed))
return (0);
if (len == 0)
return (0);
switch (keysym) {
case SDLK_BACKSPACE:
if (ed->pos == 0) {
return (0);
}
if (ed->pos == len) {
ucs[len-1] = '\0';
ed->pos--;
return (1);
}
ed->pos--;
break;
case SDLK_DELETE:
if (ed->pos == len) {
ucs[len-1] = '\0';
ed->pos--;
return (1);
}
break;
default:
break;
}
for (c = &ucs[ed->pos]; c < &ucs[len+1]; c++) {
*c = c[1];
if (*c == '\0')
break;
}
return (1);
}
static int
KillUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
size_t lenKill = 0;
Uint32 *c;
if (AG_WidgetDisabled(ed)) {
return (0);
}
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_k) && (keymod & KMOD_CTRL))
return (0);
for (c = &ucs[ed->pos]; c < &ucs[len]; c++) {
if (*c == '\n') {
break;
}
lenKill++;
}
if (lenKill == 0)
return (0);
AG_MutexLock(&killRingLock);
killRing = Realloc(killRing, (lenKill+1)*sizeof(Uint32));
memcpy(killRing, &ucs[ed->pos], lenKill*sizeof(Uint32));
killRing[lenKill] = '\0';
killRingLen = lenKill;
AG_MutexUnlock(&killRingLock);
if (ed->pos+lenKill == len) {
ucs[ed->pos] = '\0';
} else {
memmove(&ucs[ed->pos], &ucs[ed->pos+lenKill],
(len-lenKill+1 - ed->pos)*sizeof(Uint32));
}
return (1);
}
static int
YankUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
int i;
if (AG_WidgetDisabled(ed)) {
return (0);
}
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_y) && (keymod & KMOD_CTRL))
return (0);
AG_MutexLock(&killRingLock);
if (killRing == NULL) {
goto nochange;
}
if (AG_LengthUTF8FromUCS4(ucs) +
AG_LengthUTF8FromUCS4(killRing) + 1 >= bufSize) {
/* TODO truncate */
goto nochange;
}
switch (ed->encoding) {
case AG_ENCODING_UTF8:
break;
case AG_ENCODING_ASCII:
for (i = 0; i < killRingLen; i++) {
if (!isascii((int)killRing[i]))
goto nochange;
}
break;
}
if (ed->pos < len) {
memmove(&ucs[ed->pos+killRingLen], &ucs[ed->pos],
(len - ed->pos)*sizeof(Uint32));
}
memcpy(&ucs[ed->pos], killRing, killRingLen*sizeof(Uint32));
ucs[len+killRingLen] = '\0';
ed->pos += killRingLen;
AG_MutexUnlock(&killRingLock);
return (1);
nochange:
AG_MutexUnlock(&killRingLock);
return (0);
}
static int
WordBackUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
Uint32 *c;
if (ed->flags & AG_EDITABLE_NOWORDSEEK)
return (0);
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_b) && (keymod & KMOD_ALT))
return (0);
if (ed->pos > 1 && ucs[ed->pos-1] == ' ') {
ed->pos -= 2;
}
for (c = &ucs[ed->pos];
c > &ucs[0] && *c != ' ';
c--, ed->pos--)
;;
if (*c == ' ') {
ed->pos++;
}
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
WordForwUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
Uint32 *c;
if (ed->flags & AG_EDITABLE_NOWORDSEEK)
return (0);
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_f) && (keymod & KMOD_ALT))
return (0);
if (ed->pos == len) {
return (0);
}
if (len > 1 && ucs[ed->pos] == ' ') {
ed->pos++;
}
for (c = &ucs[ed->pos];
*c != '\0' && *c != ' ';
c++, ed->pos++)
;;
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
CursorHomeUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
Uint32 *c;
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_a) && (keymod & KMOD_CTRL)) {
return (0);
}
if (ed->flags & AG_EDITABLE_MULTILINE) {
if (ed->pos == 0) {
return (0);
}
for (c = &ucs[ed->pos-1];
c >= &ucs[0] && ed->pos >= 0;
c--, ed->pos--) {
if (*c == '\n')
break;
}
} else {
ed->pos = 0;
}
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
CursorEndUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
Uint32 *c;
if ((ed->flags & AG_EDITABLE_NOEMACS) &&
(keysym == SDLK_e) && (keymod & KMOD_CTRL)) {
return (0);
}
if (ed->flags & AG_EDITABLE_MULTILINE) {
if (ed->pos == len || ucs[ed->pos] == '\n') {
return (0);
}
for (c = &ucs[ed->pos+1];
c <= &ucs[len] && ed->pos <= len;
c++, ed->pos++) {
if (*c == '\n') {
ed->pos++;
break;
}
}
if (ed->pos > len) {
ed->pos = len;
}
} else {
ed->pos = len;
}
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
CursorLeftUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
if (--ed->pos < 1) {
ed->pos = 0;
}
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
CursorRightUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
if (ed->pos < len) {
ed->pos++;
}
ed->flags |= AG_EDITABLE_MARKPREF;
return (0);
}
static int
CursorUpUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
AG_EditableMoveCursor(ed, ed->xCursPref,
(ed->yCurs - ed->y - 1)*agTextFontLineSkip + 1,
1);
return (0);
}
static int
CursorDownUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
AG_EditableMoveCursor(ed, ed->xCursPref,
(ed->yCurs - ed->y + 1)*agTextFontLineSkip + 1,
1);
return (0);
}
static int
PageUpUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
AG_EditableMoveCursor(ed, ed->xCurs,
(ed->yCurs - ed->y - ed->yVis)*agTextFontLineSkip + 1,
1);
return (0);
}
static int
PageDownUTF8(AG_Editable *ed, SDLKey keysym, int keymod, Uint32 uch,
Uint32 *ucs, int len, int bufSize)
{
AG_EditableMoveCursor(ed, ed->xCurs,
(ed->yCurs - ed->y + ed->yVis)*agTextFontLineSkip + 1,
1);
return (0);
}
const struct ag_keycode_utf8 agKeymapUTF8[] = {
{ SDLK_HOME, 0, CursorHomeUTF8 },
{ SDLK_a, KMOD_CTRL, CursorHomeUTF8 },
{ SDLK_END, 0, CursorEndUTF8 },
{ SDLK_e, KMOD_CTRL, CursorEndUTF8 },
{ SDLK_LEFT, 0, CursorLeftUTF8 },
{ SDLK_RIGHT, 0, CursorRightUTF8 },
{ SDLK_UP, 0, CursorUpUTF8 },
{ SDLK_DOWN, 0, CursorDownUTF8 },
{ SDLK_PAGEUP, 0, PageUpUTF8 },
{ SDLK_PAGEDOWN, 0, PageDownUTF8 },
{ SDLK_BACKSPACE, 0, DeleteUTF8 },
{ SDLK_DELETE, 0, DeleteUTF8 },
{ SDLK_k, KMOD_CTRL, KillUTF8 },
{ SDLK_y, KMOD_CTRL, YankUTF8 },
{ SDLK_b, KMOD_ALT, WordBackUTF8 },
{ SDLK_f, KMOD_ALT, WordForwUTF8 },
{ SDLK_LAST, 0, InsertUTF8 },
};
#endif /* UTF8 */