1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-20 21:08:08 +01:00
Files
wmaker/WINGs/wtextfield.c
dan 33cc542e85 - Finished moving to the new proplist handling code in WINGs.
- Also tested the backward compatibility ability of the WINGs proplist code
  which seems to work quite well.

Starting with this moment, Window Maker no longer needs libPropList and is
now using the better and much more robust proplist code from WINGs. Also the
WINGs based proplist code is actively maintained while the old libPropList
code is practically dead and flawed by the fact that it borrowed concepts
from the UserDefaults which conflicted with the retain/release mechanism,
making some problems that libPropList had, practically unsolvable without a
complete redesign (which can be found in the more robust WINGs code).
2001-10-04 03:07:34 +00:00

1605 lines
37 KiB
C

#include "WINGsP.h"
#include "wconfig.h"
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <ctype.h>
#define CURSOR_BLINK_ON_DELAY 600
#define CURSOR_BLINK_OFF_DELAY 300
char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
typedef struct W_TextField {
W_Class widgetClass;
W_View *view;
#if 0
struct W_TextField *nextField; /* next textfield in the chain */
struct W_TextField *prevField;
#endif
char *text;
int textLen; /* size of text */
int bufferSize; /* memory allocated for text */
int viewPosition; /* position of text being shown */
int cursorPosition; /* position of the insertion cursor */
short usableWidth;
short offsetWidth; /* offset of text from border */
WMRange selection;
WMFont *font;
WMTextFieldDelegate *delegate;
#if 0
WMHandlerID timerID; /* for cursor blinking */
#endif
struct {
WMAlignment alignment:2;
unsigned int bordered:1;
unsigned int beveled:1;
unsigned int enabled:1;
unsigned int focused:1;
unsigned int cursorOn:1;
unsigned int secure:1; /* password entry style */
unsigned int pointerGrabbed:1;
unsigned int ownsSelection:1;
unsigned int waitingSelection:1; /* requested selection, but
* didnt get yet */
/**/
unsigned int notIllegalMovement:1;
} flags;
} TextField;
#define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
if ((T)->delegate && (T)->delegate->C)\
(*(T)->delegate->C)((T)->delegate,notif);\
WMPostNotification(notif);\
WMReleaseNotification(notif);}
#define MIN_TEXT_BUFFER 2
#define TEXT_BUFFER_INCR 8
#define WM_EMACSKEYMASK ControlMask
#define WM_EMACSKEY_LEFT XK_b
#define WM_EMACSKEY_RIGHT XK_f
#define WM_EMACSKEY_HOME XK_a
#define WM_EMACSKEY_END XK_e
#define WM_EMACSKEY_BS XK_h
#define WM_EMACSKEY_DEL XK_d
#define DEFAULT_WIDTH 60
#define DEFAULT_HEIGHT 20
#define DEFAULT_BORDERED True
#define DEFAULT_ALIGNMENT WALeft
static void destroyTextField(TextField *tPtr);
static void paintTextField(TextField *tPtr);
static void handleEvents(XEvent *event, void *data);
static void handleTextFieldActionEvents(XEvent *event, void *data);
static void didResizeTextField();
struct W_ViewDelegate _TextFieldViewDelegate = {
NULL,
NULL,
didResizeTextField,
NULL,
NULL
};
static void lostHandler(WMView *view, Atom selection, void *cdata);
static WMData *requestHandler(WMView *view, Atom selection, Atom target,
void *cdata, Atom *type);
static WMSelectionProcs selectionHandler = {
requestHandler,
lostHandler,
NULL
};
#define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
&((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
#define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
&((tPtr)->text[(start)]), (end) - (start) + 1))
static void
normalizeRange(TextField *tPtr, WMRange *range)
{
if (range->position < 0 && range->count < 0)
range->count = 0;
if (range->count == 0) {
/*range->position = 0; why is this?*/
return;
}
/* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
if (range->count < 0) { /* && range->position >= 0 */
if (range->position + range->count < 0) {
range->count = range->position;
range->position = 0;
} else {
range->count = -range->count;
range->position -= range->count;
}
/* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
} else if (range->position < 0) { /* && range->count > 0 */
if (range->position + range->count < 0) {
range->position = range->count = 0;
} else {
range->count += range->position;
range->position = 0;
}
}
if (range->position + range->count > tPtr->textLen)
range->count = tPtr->textLen - range->position;
}
static void
memmv(char *dest, char *src, int size)
{
int i;
if (dest > src) {
for (i=size-1; i>=0; i--) {
dest[i] = src[i];
}
} else if (dest < src) {
for (i=0; i<size; i++) {
dest[i] = src[i];
}
}
}
static int
incrToFit(TextField *tPtr)
{
int vp = tPtr->viewPosition;
while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
tPtr->viewPosition++;
}
return vp!=tPtr->viewPosition;
}
static int
incrToFit2(TextField *tPtr)
{
int vp = tPtr->viewPosition;
while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
>= tPtr->usableWidth)
tPtr->viewPosition++;
return vp!=tPtr->viewPosition;
}
static void
decrToFit(TextField *tPtr)
{
while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
&& tPtr->viewPosition>0)
tPtr->viewPosition--;
}
#undef TEXT_WIDTH
#undef TEXT_WIDTH2
static WMData*
requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
Atom *type)
{
TextField *tPtr = view->self;
int count;
Display *dpy = tPtr->view->screen->display;
Atom _TARGETS;
Atom TEXT = XInternAtom(dpy, "TEXT", False);
Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
WMData *data;
count = tPtr->selection.count < 0
? tPtr->selection.position + tPtr->selection.count
: tPtr->selection.position;
if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
data = WMCreateDataWithBytes(&(tPtr->text[count]),
abs(tPtr->selection.count));
WMSetDataFormat(data, 8);
*type = target;
return data;
}
_TARGETS = XInternAtom(dpy, "TARGETS", False);
if (target == _TARGETS) {
Atom *ptr;
ptr = wmalloc(4 * sizeof(Atom));
ptr[0] = _TARGETS;
ptr[1] = XA_STRING;
ptr[2] = TEXT;
ptr[3] = COMPOUND_TEXT;
data = WMCreateDataWithBytes(ptr, 4*4);
WMSetDataFormat(data, 32);
*type = target;
return data;
}
return NULL;
}
static void
lostHandler(WMView *view, Atom selection, void *cdata)
{
TextField *tPtr = (WMTextField*)view->self;
tPtr->flags.ownsSelection = 0;
tPtr->selection.count = 0;
paintTextField(tPtr);
}
static void
_notification(void *observerData, WMNotification *notification)
{
WMTextField *to = (WMTextField*)observerData;
WMTextField *tw = (WMTextField*)WMGetNotificationClientData(notification);
if (to != tw) lostHandler(to->view, XA_PRIMARY, NULL);
}
WMTextField*
WMCreateTextField(WMWidget *parent)
{
TextField *tPtr;
tPtr = wmalloc(sizeof(TextField));
memset(tPtr, 0, sizeof(TextField));
tPtr->widgetClass = WC_TextField;
tPtr->view = W_CreateView(W_VIEW(parent));
if (!tPtr->view) {
wfree(tPtr);
return NULL;
}
tPtr->view->self = tPtr;
tPtr->view->delegate = &_TextFieldViewDelegate;
tPtr->view->attribFlags |= CWCursor;
tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
tPtr->text = wmalloc(MIN_TEXT_BUFFER);
tPtr->text[0] = 0;
tPtr->textLen = 0;
tPtr->bufferSize = MIN_TEXT_BUFFER;
tPtr->flags.enabled = 1;
WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
|FocusChangeMask, handleEvents, tPtr);
tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
tPtr->flags.bordered = DEFAULT_BORDERED;
tPtr->flags.beveled = True;
tPtr->flags.alignment = DEFAULT_ALIGNMENT;
tPtr->offsetWidth =
WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
|ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
handleTextFieldActionEvents, tPtr);
WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
tPtr->flags.cursorOn = 1;
return tPtr;
}
void
WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->delegate = delegate;
}
void
WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
{
int len;
CHECK_CLASS(tPtr, WC_TextField);
if (!text)
return;
len = strlen(text);
/* check if buffer will hold the text */
if (len + tPtr->textLen >= tPtr->bufferSize) {
tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
}
if (position < 0 || position >= tPtr->textLen) {
/* append the text at the end */
strcat(tPtr->text, text);
incrToFit(tPtr);
tPtr->textLen += len;
tPtr->cursorPosition += len;
} else {
/* insert text at position */
memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
tPtr->textLen-position+1);
memcpy(&(tPtr->text[position]), text, len);
tPtr->textLen += len;
if (position >= tPtr->cursorPosition) {
tPtr->cursorPosition += len;
incrToFit2(tPtr);
} else {
incrToFit(tPtr);
}
}
paintTextField(tPtr);
}
void
WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
{
CHECK_CLASS(tPtr, WC_TextField);
normalizeRange(tPtr, &range);
if (!range.count)
return;
memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
tPtr->textLen - (range.position+range.count) + 1);
tPtr->textLen -= range.count;
/* try to keep cursorPosition at the same place */
tPtr->viewPosition -= range.count;
if (tPtr->viewPosition < 0)
tPtr->viewPosition = 0;
tPtr->cursorPosition = range.position;
decrToFit(tPtr);
paintTextField(tPtr);
}
char*
WMGetTextFieldText(WMTextField *tPtr)
{
CHECK_CLASS(tPtr, WC_TextField);
return wstrdup(tPtr->text);
}
void
WMSetTextFieldText(WMTextField *tPtr, char *text)
{
CHECK_CLASS(tPtr, WC_TextField);
if ((text && strcmp(tPtr->text, text) == 0) ||
(!text && tPtr->textLen == 0))
return;
if (text==NULL) {
tPtr->text[0] = 0;
tPtr->textLen = 0;
} else {
tPtr->textLen = strlen(text);
if (tPtr->textLen >= tPtr->bufferSize) {
tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
}
strcpy(tPtr->text, text);
}
tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
tPtr->viewPosition = 0;
tPtr->selection.count = 0;
if (tPtr->view->flags.realized)
paintTextField(tPtr);
}
void
WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->flags.alignment = alignment;
if (alignment!=WALeft) {
wwarning("only left alignment is supported in textfields");
return;
}
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
void
WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->flags.bordered = bordered;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
void
WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->flags.beveled = flag;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
void
WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->flags.secure = flag;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
Bool
WMGetTextFieldEditable(WMTextField *tPtr)
{
CHECK_CLASS(tPtr, WC_TextField);
return tPtr->flags.enabled;
}
void
WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
{
CHECK_CLASS(tPtr, WC_TextField);
tPtr->flags.enabled = flag;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
void
WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
{
CHECK_CLASS(tPtr, WC_TextField);
if (tPtr->flags.enabled) {
normalizeRange(tPtr, &range);
tPtr->selection = range;
tPtr->cursorPosition = range.position + range.count;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
}
void
WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
{
CHECK_CLASS(tPtr, WC_TextField);
if (tPtr->flags.enabled) {
if (position > tPtr->textLen)
position = tPtr->textLen;
tPtr->cursorPosition = position;
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
}
void
WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
{
CHECK_CLASS(tPtr, WC_TextField);
if (next == NULL) {
if (tPtr->view->nextFocusChain)
tPtr->view->nextFocusChain->prevFocusChain = NULL;
tPtr->view->nextFocusChain = NULL;
return;
}
CHECK_CLASS(next, WC_TextField);
if (tPtr->view->nextFocusChain)
tPtr->view->nextFocusChain->prevFocusChain = NULL;
if (next->view->prevFocusChain)
next->view->prevFocusChain->nextFocusChain = NULL;
tPtr->view->nextFocusChain = next->view;
next->view->prevFocusChain = tPtr->view;
}
void
WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
{
CHECK_CLASS(tPtr, WC_TextField);
if (prev == NULL) {
if (tPtr->view->prevFocusChain)
tPtr->view->prevFocusChain->nextFocusChain = NULL;
tPtr->view->prevFocusChain = NULL;
return;
}
CHECK_CLASS(prev, WC_TextField);
if (tPtr->view->prevFocusChain)
tPtr->view->prevFocusChain->nextFocusChain = NULL;
if (prev->view->nextFocusChain)
prev->view->nextFocusChain->prevFocusChain = NULL;
tPtr->view->prevFocusChain = prev->view;
prev->view->nextFocusChain = tPtr->view;
}
void
WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
{
CHECK_CLASS(tPtr, WC_TextField);
if (tPtr->font)
WMReleaseFont(tPtr->font);
tPtr->font = WMRetainFont(font);
tPtr->offsetWidth =
WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
if (tPtr->view->flags.realized) {
paintTextField(tPtr);
}
}
WMFont*
WMGetTextFieldFont(WMTextField *tPtr)
{
return tPtr->font;
}
static void
didResizeTextField(W_ViewDelegate *self, WMView *view)
{
WMTextField *tPtr = (WMTextField*)view->self;
tPtr->offsetWidth =
WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth /*+ 2*/;
}
static char*
makeHiddenString(int length)
{
char *data = wmalloc(length+1);
memset(data, '*', length);
data[length] = '\0';
return data;
}
static void
paintCursor(TextField *tPtr)
{
int cx;
WMScreen *screen = tPtr->view->screen;
int textWidth;
char *text;
if (tPtr->flags.secure)
text = makeHiddenString(strlen(tPtr->text));
else
text = tPtr->text;
cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
tPtr->cursorPosition-tPtr->viewPosition);
switch (tPtr->flags.alignment) {
case WARight:
textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
if (textWidth < tPtr->usableWidth)
cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
else
cx += tPtr->offsetWidth + 1;
break;
case WALeft:
cx += tPtr->offsetWidth + 1;
break;
case WAJustified:
/* not supported */
case WACenter:
textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
if (textWidth < tPtr->usableWidth)
cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
else
cx += tPtr->offsetWidth;
break;
}
/*
XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
cx, tPtr->offsetWidth, 1,
tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
printf("%d %d\n",cx,tPtr->cursorPosition);
*/
XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
cx, tPtr->offsetWidth, cx,
tPtr->view->size.height - tPtr->offsetWidth - 1);
if (tPtr->flags.secure)
wfree(text);
}
static void
drawRelief(WMView *view, Bool beveled)
{
WMScreen *scr = view->screen;
Display *dpy = scr->display;
GC wgc;
GC lgc;
GC dgc;
int width = view->size.width;
int height = view->size.height;
dgc = WMColorGC(scr->darkGray);
if (!beveled) {
XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
return;
}
wgc = WMColorGC(scr->white);
lgc = WMColorGC(scr->gray);
/* top left */
XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
/* bottom right */
XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
}
static void
paintTextField(TextField *tPtr)
{
W_Screen *screen = tPtr->view->screen;
W_View *view = tPtr->view;
W_View viewbuffer;
int tx, ty, tw, th;
int rx;
int bd;
int totalWidth;
char *text;
Pixmap drawbuffer;
if (!view->flags.realized || !view->flags.mapped)
return;
if (!tPtr->flags.bordered) {
bd = 0;
} else {
bd = 2;
}
if (tPtr->flags.secure) {
text = makeHiddenString(strlen(tPtr->text));
} else {
text = tPtr->text;
}
totalWidth = tPtr->view->size.width - 2*bd;
drawbuffer = XCreatePixmap(screen->display, view->window,
view->size.width, view->size.height, screen->depth);
XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
0,0, view->size.width,view->size.height);
/* this is quite dirty */
viewbuffer.screen = view->screen;
viewbuffer.size = view->size;
viewbuffer.window = drawbuffer;
if (tPtr->textLen > 0) {
tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
tPtr->textLen - tPtr->viewPosition);
th = WMFontHeight(tPtr->font);
ty = tPtr->offsetWidth;
switch (tPtr->flags.alignment) {
case WALeft:
tx = tPtr->offsetWidth + 1;
if (tw < tPtr->usableWidth)
XFillRectangle(screen->display, drawbuffer,
WMColorGC(screen->white),
bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
break;
case WACenter:
tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
if (tw < tPtr->usableWidth)
XClearArea(screen->display, view->window, bd, bd,
totalWidth, view->size.height-2*bd, False);
break;
default:
case WARight:
tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
if (tw < tPtr->usableWidth)
XClearArea(screen->display, view->window, bd, bd,
totalWidth-tw, view->size.height-2*bd, False);
break;
}
if (!tPtr->flags.enabled)
WMSetColorInGC(screen->darkGray, screen->textFieldGC);
WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
tPtr->font, tx, ty,
&(text[tPtr->viewPosition]),
tPtr->textLen - tPtr->viewPosition);
if (tPtr->selection.count) {
int count,count2;
count = tPtr->selection.count < 0
? tPtr->selection.position + tPtr->selection.count
: tPtr->selection.position;
count2 = abs(tPtr->selection.count);
if (count < tPtr->viewPosition) {
count2 = abs(count2 - abs(tPtr->viewPosition - count));
count = tPtr->viewPosition;
}
rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
- WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
XSetBackground(screen->display, screen->textFieldGC,
screen->gray->color.pixel);
WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
tPtr->font, rx, ty, &(text[count]),
count2);
XSetBackground(screen->display, screen->textFieldGC,
screen->white->color.pixel);
}
if (!tPtr->flags.enabled)
WMSetColorInGC(screen->black, screen->textFieldGC);
} else {
XFillRectangle(screen->display, drawbuffer,
WMColorGC(screen->white),
bd,bd, totalWidth,view->size.height-2*bd);
}
/* draw relief */
if (tPtr->flags.bordered) {
drawRelief(&viewbuffer, tPtr->flags.beveled);
}
if (tPtr->flags.secure)
wfree(text);
XCopyArea(screen->display, drawbuffer, view->window,
screen->copyGC, 0,0, view->size.width,
view->size.height,0,0);
XFreePixmap(screen->display, drawbuffer);
/* draw cursor */
if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
paintCursor(tPtr);
}
}
#if 0
static void
blinkCursor(void *data)
{
TextField *tPtr = (TextField*)data;
if (tPtr->flags.cursorOn) {
tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
data);
} else {
tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
data);
}
paintCursor(tPtr);
tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
}
#endif
static void
handleEvents(XEvent *event, void *data)
{
TextField *tPtr = (TextField*)data;
CHECK_CLASS(data, WC_TextField);
switch (event->type) {
case FocusIn:
if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
return;
tPtr->flags.focused = 1;
#if 0
if (!tPtr->timerID) {
tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
blinkCursor, tPtr);
}
#endif
paintTextField(tPtr);
NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
tPtr->flags.notIllegalMovement = 0;
break;
case FocusOut:
tPtr->flags.focused = 0;
#if 0
if (tPtr->timerID)
WMDeleteTimerHandler(tPtr->timerID);
tPtr->timerID = NULL;
#endif
paintTextField(tPtr);
if (!tPtr->flags.notIllegalMovement) {
NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
(void*)WMIllegalTextMovement);
}
break;
case Expose:
if (event->xexpose.count!=0)
break;
paintTextField(tPtr);
break;
case DestroyNotify:
destroyTextField(tPtr);
break;
}
}
static void
handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
{
char buffer[64];
KeySym ksym;
char *textEvent = NULL;
void *data = NULL;
int count, refresh = 0;
int control_pressed = 0;
int cancelSelection = 1;
Bool shifted, controled, modified;
Bool relay = True;
/*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
control_pressed = 1;
shifted = event->xkey.state & ShiftMask;
controled = event->xkey.state & ControlMask;
if ((event->xkey.state & (ShiftMask|ControlMask)) != 0) {
modified = True;
} else {
modified = False;
}
count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
buffer[count] = '\0';
switch (ksym) {
case XK_Tab:
#ifdef XK_ISO_Left_Tab
case XK_ISO_Left_Tab:
#endif
if (!controled && !modified) {
if (shifted) {
if (tPtr->view->prevFocusChain) {
W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
tPtr->view->prevFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void*)WMBacktabTextMovement;
} else {
if (tPtr->view->nextFocusChain) {
W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
tPtr->view->nextFocusChain);
tPtr->flags.notIllegalMovement = 1;
}
data = (void*)WMTabTextMovement;
}
textEvent = WMTextDidEndEditingNotification;
relay = False;
}
break;
case XK_Escape:
if (!shifted && !controled && !modified) {
data = (void*)WMEscapeTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
}
break;
case XK_Return:
if (!shifted && !controled && !modified) {
data = (void*)WMReturnTextMovement;
textEvent = WMTextDidEndEditingNotification;
relay = False;
}
break;
case WM_EMACSKEY_LEFT:
if (!control_pressed)
goto normal_key;
else
modified = False;
#ifdef XK_KP_Left
case XK_KP_Left:
#endif
case XK_Left:
if (!modified) {
if (tPtr->cursorPosition > 0) {
paintCursor(tPtr);
if (event->xkey.state & ControlMask) {
int i = tPtr->cursorPosition - 1;
while (i > 0 && tPtr->text[i] != ' ') i--;
while (i > 0 && tPtr->text[i] == ' ') i--;
tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
} else
tPtr->cursorPosition--;
if (tPtr->cursorPosition < tPtr->viewPosition) {
tPtr->viewPosition = tPtr->cursorPosition;
refresh = 1;
} else
paintCursor(tPtr);
}
if (event->xkey.state & ShiftMask)
cancelSelection = 0;
relay = False;
}
break;
case WM_EMACSKEY_RIGHT:
if (!control_pressed)
goto normal_key;
else
modified = False;
#ifdef XK_KP_Right
case XK_KP_Right:
#endif
case XK_Right:
if (!modified) {
if (tPtr->cursorPosition < tPtr->textLen) {
paintCursor(tPtr);
if (event->xkey.state & ControlMask) {
int i = tPtr->cursorPosition;
while (tPtr->text[i] && tPtr->text[i] != ' ') i++;
while (tPtr->text[i] == ' ') i++;
tPtr->cursorPosition = i;
} else {
tPtr->cursorPosition++;
}
while (WMWidthOfString(tPtr->font,
&(tPtr->text[tPtr->viewPosition]),
tPtr->cursorPosition-tPtr->viewPosition)
> tPtr->usableWidth) {
tPtr->viewPosition++;
refresh = 1;
}
if (!refresh)
paintCursor(tPtr);
}
if (event->xkey.state & ShiftMask)
cancelSelection = 0;
relay = False;
}
break;
case WM_EMACSKEY_HOME:
if (!control_pressed)
goto normal_key;
else {
modified = False;
controled = False;
}
#ifdef XK_KP_Home
case XK_KP_Home:
#endif
case XK_Home:
if (!modified && !controled) {
if (tPtr->cursorPosition > 0) {
paintCursor(tPtr);
tPtr->cursorPosition = 0;
if (tPtr->viewPosition > 0) {
tPtr->viewPosition = 0;
refresh = 1;
} else
paintCursor(tPtr);
}
if (event->xkey.state & ShiftMask)
cancelSelection = 0;
relay = False;
}
break;
case WM_EMACSKEY_END:
if (!control_pressed)
goto normal_key;
else {
modified = False;
controled = False;
}
#ifdef XK_KP_End
case XK_KP_End:
#endif
case XK_End:
if (!modified && !controled) {
if (tPtr->cursorPosition < tPtr->textLen) {
paintCursor(tPtr);
tPtr->cursorPosition = tPtr->textLen;
tPtr->viewPosition = 0;
while (WMWidthOfString(tPtr->font,
&(tPtr->text[tPtr->viewPosition]),
tPtr->textLen-tPtr->viewPosition)
> tPtr->usableWidth) {
tPtr->viewPosition++;
refresh = 1;
}
if (!refresh)
paintCursor(tPtr);
}
if (event->xkey.state & ShiftMask)
cancelSelection = 0;
relay = False;
}
break;
case WM_EMACSKEY_BS:
if (!control_pressed)
goto normal_key;
else {
modified = False;
controled = False;
shifted = False;
}
case XK_BackSpace:
if (!modified && !shifted && !controled) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void*)WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition > 0) {
WMRange range;
range.position = tPtr->cursorPosition - 1;
range.count = 1;
WMDeleteTextFieldRange(tPtr, range);
data = (void*)WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
relay = False;
}
break;
case WM_EMACSKEY_DEL:
if (!control_pressed)
goto normal_key;
else {
modified = False;
controled = False;
shifted = False;
}
#ifdef XK_KP_Delete
case XK_KP_Delete:
#endif
case XK_Delete:
if (!modified && !controled && !shifted) {
if (tPtr->selection.count) {
WMDeleteTextFieldRange(tPtr, tPtr->selection);
data = (void*)WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
} else if (tPtr->cursorPosition < tPtr->textLen) {
WMRange range;
range.position = tPtr->cursorPosition;
range.count = 1;
WMDeleteTextFieldRange(tPtr, range);
data = (void*)WMDeleteTextEvent;
textEvent = WMTextDidChangeNotification;
}
relay = False;
}
break;
normal_key:
default:
if (!controled) {
if (count > 0 && !iscntrl(buffer[0])) {
if (tPtr->selection.count)
WMDeleteTextFieldRange(tPtr, tPtr->selection);
WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
data = (void*)WMInsertTextEvent;
textEvent = WMTextDidChangeNotification;
relay = False;
}
}
break;
}
if (relay) {
WMRelayToNextResponder(W_VIEW(tPtr), event);
return;
}
/* Do not allow text selection in secure text fields */
if (cancelSelection || tPtr->flags.secure) {
lostHandler(tPtr->view, XA_PRIMARY, NULL);
if (tPtr->selection.count) {
tPtr->selection.count = 0;
refresh = 1;
}
tPtr->selection.position = tPtr->cursorPosition;
} else {
if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
WMPostNotificationName("_lostOwnership", NULL, tPtr);
refresh = 1;
}
}
/*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
if (textEvent) {
WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
if (tPtr->delegate) {
if (textEvent==WMTextDidBeginEditingNotification &&
tPtr->delegate->didBeginEditing)
(*tPtr->delegate->didBeginEditing)(tPtr->delegate, notif);
else if (textEvent==WMTextDidEndEditingNotification &&
tPtr->delegate->didEndEditing)
(*tPtr->delegate->didEndEditing)(tPtr->delegate, notif);
else if (textEvent==WMTextDidChangeNotification &&
tPtr->delegate->didChange)
(*tPtr->delegate->didChange)(tPtr->delegate, notif);
}
WMPostNotification(notif);
WMReleaseNotification(notif);
}
if (refresh)
paintTextField(tPtr);
/*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
}
static int
pointToCursorPosition(TextField *tPtr, int x)
{
int a, b, mid;
int tw;
if (tPtr->flags.bordered)
x -= 2;
a = tPtr->viewPosition;
b = tPtr->viewPosition + tPtr->textLen;
if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
tPtr->textLen - tPtr->viewPosition) < x)
return tPtr->textLen;
while (a < b && b-a>1) {
mid = (a+b)/2;
tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
mid - tPtr->viewPosition);
if (tw > x)
b = mid;
else if (tw < x)
a = mid;
else
return mid;
}
return (a+b)/2;
}
static void
pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
void *cdata, WMData *data)
{
TextField *tPtr = (TextField*)view->self;
char *str;
tPtr->flags.waitingSelection = 0;
if (data != NULL) {
str = (char*)WMDataBytes(data);
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void*)WMInsertTextEvent);
} else {
int n;
str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
if (str != NULL) {
str[n] = 0;
WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
XFree(str);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void*)WMInsertTextEvent);
}
}
}
static void
handleTextFieldActionEvents(XEvent *event, void *data)
{
TextField *tPtr = (TextField*)data;
static int move = 0;
static Time lastButtonReleasedEvent = 0;
Display *dpy = event->xany.display;
CHECK_CLASS(data, WC_TextField);
switch (event->type) {
case KeyPress:
if (tPtr->flags.waitingSelection) {
return;
}
if (tPtr->flags.enabled && tPtr->flags.focused) {
handleTextFieldKeyPress(tPtr, event);
XDefineCursor(dpy, W_VIEW(tPtr)->window,
W_VIEW(tPtr)->screen->invisibleCursor);
tPtr->flags.pointerGrabbed = 1;
}
break;
case MotionNotify:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = 0;
XDefineCursor(dpy, W_VIEW(tPtr)->window,
W_VIEW(tPtr)->screen->textCursor);
}
if (tPtr->flags.waitingSelection) {
return;
}
if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
tPtr->usableWidth) {
if (WMWidthOfString(tPtr->font,
&(tPtr->text[tPtr->viewPosition]),
tPtr->cursorPosition-tPtr->viewPosition)
> tPtr->usableWidth) {
tPtr->viewPosition++;
}
} else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
paintCursor(tPtr);
tPtr->viewPosition--;
}
tPtr->cursorPosition =
pointToCursorPosition(tPtr, event->xmotion.x);
/* Do not allow text selection in secure textfields */
if (tPtr->flags.secure) {
tPtr->selection.position = tPtr->cursorPosition;
}
tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
if (tPtr->selection.count != 0) {
if (!tPtr->flags.ownsSelection) {
WMCreateSelectionHandler(tPtr->view,
XA_PRIMARY,
event->xbutton.time,
&selectionHandler, NULL);
tPtr->flags.ownsSelection = 1;
}
}
paintCursor(tPtr);
paintTextField(tPtr);
}
break;
case ButtonPress:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = 0;
XDefineCursor(dpy, W_VIEW(tPtr)->window,
W_VIEW(tPtr)->screen->textCursor);
break;
}
if (tPtr->flags.waitingSelection) {
break;
}
move = 1;
switch (tPtr->flags.alignment) {
int textWidth;
case WARight:
textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
if (tPtr->flags.enabled && !tPtr->flags.focused) {
WMSetFocusToWidget(tPtr);
} else if (tPtr->flags.focused) {
tPtr->selection.position = tPtr->cursorPosition;
tPtr->selection.count = 0;
}
if (textWidth < tPtr->usableWidth) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x - tPtr->usableWidth
+ textWidth);
} else tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x);
paintTextField(tPtr);
break;
case WALeft:
if (tPtr->flags.enabled && !tPtr->flags.focused) {
WMSetFocusToWidget(tPtr);
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x);
paintTextField(tPtr);
} else if (tPtr->flags.focused
&& event->xbutton.button == Button1) {
tPtr->cursorPosition = pointToCursorPosition(tPtr,
event->xbutton.x);
tPtr->selection.position = tPtr->cursorPosition;
tPtr->selection.count = 0;
paintTextField(tPtr);
}
if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
char *text;
int n;
if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
event->xbutton.time,
pasteText, NULL)) {
text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
if (text) {
text[n] = 0;
WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
XFree(text);
NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
(void*)WMInsertTextEvent);
}
} else {
tPtr->flags.waitingSelection = 1;
}
}
break;
default:
break;
}
break;
case ButtonRelease:
if (tPtr->flags.pointerGrabbed) {
tPtr->flags.pointerGrabbed = 0;
XDefineCursor(dpy, W_VIEW(tPtr)->window,
W_VIEW(tPtr)->screen->textCursor);
}
if (tPtr->flags.waitingSelection) {
break;
}
if (tPtr->selection.count != 0) {
int start, count;
XRotateBuffers(dpy, 1);
count = abs(tPtr->selection.count);
if (tPtr->selection.count < 0)
start = tPtr->selection.position - count;
else
start = tPtr->selection.position;
XStoreBuffer(dpy, &tPtr->text[start], count, 0);
}
move = 0;
if (!tPtr->flags.secure &&
event->xbutton.time - lastButtonReleasedEvent
<= WINGsConfiguration.doubleClickDelay) {
tPtr->selection.position = 0;
tPtr->selection.count = tPtr->textLen;
paintTextField(tPtr);
if (!tPtr->flags.ownsSelection) {
WMCreateSelectionHandler(tPtr->view,
XA_PRIMARY,
event->xbutton.time,
&selectionHandler, NULL);
tPtr->flags.ownsSelection = 1;
}
WMPostNotificationName("_lostOwnership", NULL, tPtr);
}
lastButtonReleasedEvent = event->xbutton.time;
break;
}
}
static void
destroyTextField(TextField *tPtr)
{
#if 0
if (tPtr->timerID)
WMDeleteTimerHandler(tPtr->timerID);
#endif
WMReleaseFont(tPtr->font);
WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
WMRemoveNotificationObserver(tPtr);
if (tPtr->text)
wfree(tPtr->text);
wfree(tPtr);
}