mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-20 21:08:08 +01:00
- 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).
1605 lines
37 KiB
C
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);
|
|
}
|