Update Serbian translation from master branch
[wmaker-crm.git] / WINGs / wtextfield.c
blobfeb1d189c6393df2404629509717baf227da283c
2 #include "WINGsP.h"
3 #include "wconfig.h"
5 #include <X11/keysym.h>
6 #include <X11/Xatom.h>
8 #include <ctype.h>
10 #define CURSOR_BLINK_ON_DELAY 600
11 #define CURSOR_BLINK_OFF_DELAY 300
13 const char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
14 const char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
15 const char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
17 typedef struct W_TextField {
18 W_Class widgetClass;
19 W_View *view;
21 #if 0
22 struct W_TextField *nextField; /* next textfield in the chain */
23 struct W_TextField *prevField;
24 #endif
26 char *text;
27 int textLen; /* size of text */
28 int bufferSize; /* memory allocated for text */
30 int viewPosition; /* position of text being shown */
32 int cursorPosition; /* position of the insertion cursor */
34 short usableWidth;
35 short offsetWidth; /* offset of text from border */
37 WMRange selection;
39 WMFont *font;
41 WMTextFieldDelegate *delegate;
43 WMHandlerID timerID; /* for cursor blinking */
45 struct {
46 WMAlignment alignment:2;
48 unsigned int bordered:1;
50 unsigned int beveled:1;
52 unsigned int enabled:1;
54 unsigned int focused:1;
56 unsigned int cursorOn:1;
58 unsigned int secure:1; /* password entry style */
60 unsigned int pointerGrabbed:1;
62 unsigned int ownsSelection:1;
64 unsigned int waitingSelection:1; /* requested selection, but
65 * didnt get yet */
67 unsigned int notIllegalMovement:1;
68 } flags;
69 } TextField;
71 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
72 if ((T)->delegate && (T)->delegate->C)\
73 (*(T)->delegate->C)((T)->delegate,notif);\
74 WMPostNotification(notif);\
75 WMReleaseNotification(notif);}
77 #define MIN_TEXT_BUFFER 2
78 #define TEXT_BUFFER_INCR 8
80 #define WM_EMACSKEYMASK ControlMask
82 #define WM_EMACSKEY_LEFT XK_b
83 #define WM_EMACSKEY_RIGHT XK_f
84 #define WM_EMACSKEY_HOME XK_a
85 #define WM_EMACSKEY_END XK_e
86 #define WM_EMACSKEY_BS XK_h
87 #define WM_EMACSKEY_DEL XK_d
89 #define DEFAULT_WIDTH 60
90 #define DEFAULT_HEIGHT 20
91 #define DEFAULT_BORDERED True
92 #define DEFAULT_ALIGNMENT WALeft
94 static void destroyTextField(TextField * tPtr);
95 static void paintTextField(TextField * tPtr);
97 static void handleEvents(XEvent * event, void *data);
98 static void handleTextFieldActionEvents(XEvent * event, void *data);
99 static void didResizeTextField(W_ViewDelegate * self, WMView * view);
101 struct W_ViewDelegate _TextFieldViewDelegate = {
102 NULL,
103 NULL,
104 didResizeTextField,
105 NULL,
106 NULL
109 static void lostSelection(WMView * view, Atom selection, void *cdata);
111 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type);
113 static WMSelectionProcs selectionHandler = {
114 requestHandler,
115 lostSelection,
116 NULL
119 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
120 &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
122 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
123 &((tPtr)->text[(start)]), (end) - (start)))
125 static inline int oneUTF8CharBackward(const char *str, int len)
127 const unsigned char *ustr = (const unsigned char *)str;
128 int pos = 0;
130 while (len-- > 0 && ustr[--pos] >= 0x80 && ustr[pos] <= 0xbf) ;
131 return pos;
134 static inline int oneUTF8CharForward(const char *str, int len)
136 const unsigned char *ustr = (const unsigned char *)str;
137 int pos = 0;
139 while (len-- > 0 && ustr[++pos] >= 0x80 && ustr[pos] <= 0xbf) ;
140 return pos;
143 // find the beginning of the UTF8 char pointed by str
144 static inline int seekUTF8CharStart(const char *str, int len)
146 const unsigned char *ustr = (const unsigned char *)str;
147 int pos = 0;
149 while (len-- > 0 && ustr[pos] >= 0x80 && ustr[pos] <= 0xbf)
150 --pos;
151 return pos;
154 static void normalizeRange(TextField * tPtr, WMRange * range)
156 if (range->position < 0 && range->count < 0)
157 range->count = 0;
159 if (range->count == 0) {
160 /*range->position = 0; why is this? */
161 return;
164 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
165 if (range->count < 0) { /* && range->position >= 0 */
166 if (range->position + range->count < 0) {
167 range->count = range->position;
168 range->position = 0;
169 } else {
170 range->count = -range->count;
171 range->position -= range->count;
173 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
174 } else if (range->position < 0) { /* && range->count > 0 */
175 if (range->position + range->count < 0) {
176 range->position = range->count = 0;
177 } else {
178 range->count += range->position;
179 range->position = 0;
183 if (range->position + range->count > tPtr->textLen)
184 range->count = tPtr->textLen - range->position;
187 static void memmv(char *dest, const char *src, int size)
189 int i;
191 if (dest > src) {
192 for (i = size - 1; i >= 0; i--) {
193 dest[i] = src[i];
195 } else if (dest < src) {
196 for (i = 0; i < size; i++) {
197 dest[i] = src[i];
202 static int incrToFit(TextField * tPtr)
204 int vp = tPtr->viewPosition;
206 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
207 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
208 tPtr->textLen - tPtr->viewPosition);
210 return vp != tPtr->viewPosition;
213 static int incrToFit2(TextField * tPtr)
215 int vp = tPtr->viewPosition;
217 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
218 >= tPtr->usableWidth)
219 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
220 tPtr->cursorPosition - tPtr->viewPosition);
221 return vp != tPtr->viewPosition;
224 static void decrToFit(TextField * tPtr)
226 int vp = tPtr->viewPosition;
228 while (vp > 0 && (vp += oneUTF8CharBackward(&tPtr->text[vp], vp),
229 TEXT_WIDTH(tPtr, vp)) < tPtr->usableWidth) {
230 tPtr->viewPosition = vp;
234 #undef TEXT_WIDTH
235 #undef TEXT_WIDTH2
237 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type)
239 TextField *tPtr = view->self;
240 int count;
241 Display *dpy = tPtr->view->screen->display;
242 Atom _TARGETS;
243 Atom TEXT = XInternAtom(dpy, "TEXT", False);
244 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
245 WMData *data;
247 /* Parameter not used, but tell the compiler that it is ok */
248 (void) selection;
249 (void) cdata;
251 count = tPtr->selection.count < 0
252 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
254 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
256 data = WMCreateDataWithBytes(&(tPtr->text[count]), abs(tPtr->selection.count));
257 WMSetDataFormat(data, 8);
258 *type = target;
260 return data;
263 _TARGETS = XInternAtom(dpy, "TARGETS", False);
264 if (target == _TARGETS) {
265 Atom supported_type[4];
267 supported_type[0] = _TARGETS;
268 supported_type[1] = XA_STRING;
269 supported_type[2] = TEXT;
270 supported_type[3] = COMPOUND_TEXT;
272 data = WMCreateDataWithBytes(supported_type, sizeof(supported_type));
273 WMSetDataFormat(data, 32);
275 *type = target;
276 return data;
279 return NULL;
283 static void lostSelection(WMView * view, Atom selection, void *cdata)
285 TextField *tPtr = (WMTextField *) view->self;
287 /* Parameter not used, but tell the compiler that it is ok */
288 (void) cdata;
290 if (tPtr->flags.ownsSelection) {
291 WMDeleteSelectionHandler(view, selection, CurrentTime);
292 tPtr->flags.ownsSelection = 0;
294 if (tPtr->selection.count != 0) {
295 tPtr->selection.count = 0;
296 paintTextField(tPtr);
300 static void selectionNotification(void *observerData, WMNotification * notification)
302 WMView *observerView = (WMView *) observerData;
303 WMView *newOwnerView = (WMView *) WMGetNotificationClientData(notification);
305 if (observerView != newOwnerView) {
307 //if (tPtr->flags.ownsSelection)
308 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
310 lostSelection(observerView, XA_PRIMARY, NULL);
314 static void realizeObserver(void *self, WMNotification * not)
316 /* Parameter not used, but tell the compiler that it is ok */
317 (void) not;
319 W_CreateIC(((TextField *) self)->view);
322 WMTextField *WMCreateTextField(WMWidget * parent)
324 TextField *tPtr;
326 tPtr = wmalloc(sizeof(TextField));
327 tPtr->widgetClass = WC_TextField;
329 tPtr->view = W_CreateView(W_VIEW(parent));
330 if (!tPtr->view) {
331 wfree(tPtr);
332 return NULL;
334 tPtr->view->self = tPtr;
336 tPtr->view->delegate = &_TextFieldViewDelegate;
338 tPtr->view->attribFlags |= CWCursor;
339 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
341 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
343 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
344 tPtr->textLen = 0;
345 tPtr->bufferSize = MIN_TEXT_BUFFER;
347 tPtr->flags.enabled = 1;
349 WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask | FocusChangeMask, handleEvents, tPtr);
351 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
353 tPtr->flags.bordered = DEFAULT_BORDERED;
354 tPtr->flags.beveled = True;
355 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
356 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
358 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
360 WMCreateEventHandler(tPtr->view, EnterWindowMask | LeaveWindowMask
361 | ButtonReleaseMask | ButtonPressMask | KeyPressMask | Button1MotionMask,
362 handleTextFieldActionEvents, tPtr);
364 WMAddNotificationObserver(selectionNotification, tPtr->view,
365 WMSelectionOwnerDidChangeNotification, (void *)XA_PRIMARY);
367 WMAddNotificationObserver(realizeObserver, tPtr, WMViewRealizedNotification, tPtr->view);
369 tPtr->flags.cursorOn = 1;
371 return tPtr;
374 void WMSetTextFieldDelegate(WMTextField * tPtr, WMTextFieldDelegate * delegate)
376 CHECK_CLASS(tPtr, WC_TextField);
378 tPtr->delegate = delegate;
381 WMTextFieldDelegate *WMGetTextFieldDelegate(WMTextField * tPtr)
383 CHECK_CLASS(tPtr, WC_TextField);
385 return tPtr->delegate;
388 void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
390 int len;
392 CHECK_CLASS(tPtr, WC_TextField);
394 if (!text)
395 return;
397 len = strlen(text);
399 /* check if buffer will hold the text */
400 if (len + tPtr->textLen >= tPtr->bufferSize) {
401 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
402 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
405 if (position < 0 || position >= tPtr->textLen) {
406 /* append the text at the end */
407 wstrlcat(tPtr->text, text, tPtr->bufferSize);
408 tPtr->textLen += len;
409 tPtr->cursorPosition += len;
410 incrToFit(tPtr);
411 } else {
412 /* insert text at position */
413 memmv(&(tPtr->text[position + len]), &(tPtr->text[position]), tPtr->textLen - position + 1);
415 memcpy(&(tPtr->text[position]), text, len);
417 tPtr->textLen += len;
418 if (position >= tPtr->cursorPosition) {
419 tPtr->cursorPosition += len;
420 incrToFit2(tPtr);
421 } else {
422 incrToFit(tPtr);
426 paintTextField(tPtr);
429 void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
431 CHECK_CLASS(tPtr, WC_TextField);
433 normalizeRange(tPtr, &range);
435 if (!range.count)
436 return;
438 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position + range.count]),
439 tPtr->textLen - (range.position + range.count) + 1);
441 /* better than nothing ;) */
442 if (tPtr->cursorPosition > range.position)
443 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition], tPtr->viewPosition);
444 tPtr->textLen -= range.count;
445 tPtr->cursorPosition = range.position;
447 decrToFit(tPtr);
449 paintTextField(tPtr);
452 char *WMGetTextFieldText(WMTextField * tPtr)
454 CHECK_CLASS(tPtr, WC_TextField);
456 return wstrdup(tPtr->text);
459 void WMSetTextFieldText(WMTextField * tPtr, const char *text)
461 CHECK_CLASS(tPtr, WC_TextField);
463 if ((text && strcmp(tPtr->text, text) == 0) || (!text && tPtr->textLen == 0))
464 return;
466 if (text == NULL) {
467 tPtr->text[0] = 0;
468 tPtr->textLen = 0;
469 } else {
470 tPtr->textLen = strlen(text);
472 if (tPtr->textLen >= tPtr->bufferSize) {
473 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
474 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
476 wstrlcpy(tPtr->text, text, tPtr->bufferSize);
479 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
480 tPtr->viewPosition = 0;
481 tPtr->selection.count = 0;
483 if (tPtr->view->flags.realized)
484 paintTextField(tPtr);
487 void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
489 CHECK_CLASS(tPtr, WC_TextField);
491 tPtr->flags.alignment = alignment;
493 if (alignment != WALeft) {
494 wwarning(_("only left alignment is supported in textfields"));
495 return;
498 if (tPtr->view->flags.realized) {
499 paintTextField(tPtr);
503 void WMSetTextFieldBordered(WMTextField * tPtr, Bool bordered)
505 CHECK_CLASS(tPtr, WC_TextField);
507 tPtr->flags.bordered = bordered;
509 if (tPtr->view->flags.realized) {
510 paintTextField(tPtr);
514 void WMSetTextFieldBeveled(WMTextField * tPtr, Bool flag)
516 CHECK_CLASS(tPtr, WC_TextField);
518 tPtr->flags.beveled = ((flag == 0) ? 0 : 1);
520 if (tPtr->view->flags.realized) {
521 paintTextField(tPtr);
525 void WMSetTextFieldSecure(WMTextField * tPtr, Bool flag)
527 CHECK_CLASS(tPtr, WC_TextField);
529 tPtr->flags.secure = ((flag == 0) ? 0 : 1);
531 if (tPtr->view->flags.realized) {
532 paintTextField(tPtr);
536 Bool WMGetTextFieldEditable(WMTextField * tPtr)
538 CHECK_CLASS(tPtr, WC_TextField);
540 return tPtr->flags.enabled;
543 void WMSetTextFieldEditable(WMTextField * tPtr, Bool flag)
545 CHECK_CLASS(tPtr, WC_TextField);
547 tPtr->flags.enabled = ((flag == 0) ? 0 : 1);
549 if (tPtr->view->flags.realized) {
550 paintTextField(tPtr);
554 void WMSelectTextFieldRange(WMTextField * tPtr, WMRange range)
556 CHECK_CLASS(tPtr, WC_TextField);
558 if (tPtr->flags.enabled) {
559 normalizeRange(tPtr, &range);
561 tPtr->selection = range;
563 tPtr->cursorPosition = range.position + range.count;
565 if (tPtr->view->flags.realized) {
566 paintTextField(tPtr);
571 void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position)
573 CHECK_CLASS(tPtr, WC_TextField);
575 if (tPtr->flags.enabled) {
576 if (position > tPtr->textLen)
577 position = tPtr->textLen;
579 tPtr->cursorPosition = position;
580 if (tPtr->view->flags.realized) {
581 paintTextField(tPtr);
586 unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr)
588 CHECK_CLASS(tPtr, WC_TextField);
590 return tPtr->cursorPosition;
593 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
595 CHECK_CLASS(tPtr, WC_TextField);
596 if (next == NULL) {
597 if (tPtr->view->nextFocusChain)
598 tPtr->view->nextFocusChain->prevFocusChain = NULL;
599 tPtr->view->nextFocusChain = NULL;
600 return;
603 CHECK_CLASS(next, WC_TextField);
605 if (tPtr->view->nextFocusChain)
606 tPtr->view->nextFocusChain->prevFocusChain = NULL;
607 if (next->view->prevFocusChain)
608 next->view->prevFocusChain->nextFocusChain = NULL;
610 tPtr->view->nextFocusChain = next->view;
611 next->view->prevFocusChain = tPtr->view;
614 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
616 CHECK_CLASS(tPtr, WC_TextField);
617 if (prev == NULL) {
618 if (tPtr->view->prevFocusChain)
619 tPtr->view->prevFocusChain->nextFocusChain = NULL;
620 tPtr->view->prevFocusChain = NULL;
621 return;
624 CHECK_CLASS(prev, WC_TextField);
626 if (tPtr->view->prevFocusChain)
627 tPtr->view->prevFocusChain->nextFocusChain = NULL;
628 if (prev->view->nextFocusChain)
629 prev->view->nextFocusChain->prevFocusChain = NULL;
631 tPtr->view->prevFocusChain = prev->view;
632 prev->view->nextFocusChain = tPtr->view;
635 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
637 CHECK_CLASS(tPtr, WC_TextField);
639 if (tPtr->font)
640 WMReleaseFont(tPtr->font);
641 tPtr->font = WMRetainFont(font);
643 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
645 if (tPtr->view->flags.realized) {
646 paintTextField(tPtr);
650 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
652 return tPtr->font;
655 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
657 WMTextField *tPtr = (WMTextField *) view->self;
659 /* Parameter not used, but tell the compiler that it is ok */
660 (void) self;
662 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
664 tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth;
667 static char *makeHiddenString(int length)
669 char *data = wmalloc(length + 1);
671 memset(data, '*', length);
672 data[length] = '\0';
674 return data;
677 static void paintCursor(TextField * tPtr)
679 int cx;
680 WMScreen *screen = tPtr->view->screen;
681 int textWidth;
682 char *text;
684 if (tPtr->flags.secure)
685 text = makeHiddenString(strlen(tPtr->text));
686 else
687 text = tPtr->text;
689 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
691 switch (tPtr->flags.alignment) {
692 case WARight:
693 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
694 if (textWidth < tPtr->usableWidth)
695 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
696 else
697 cx += tPtr->offsetWidth + 1;
698 break;
699 case WALeft:
700 cx += tPtr->offsetWidth + 1;
701 break;
702 case WAJustified:
703 /* not supported */
704 case WACenter:
705 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
706 if (textWidth < tPtr->usableWidth)
707 cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
708 else
709 cx += tPtr->offsetWidth;
710 break;
713 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
714 cx, tPtr->offsetWidth, 1,
715 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
716 printf("%d %d\n",cx,tPtr->cursorPosition);
719 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
720 cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
722 W_SetPreeditPositon(tPtr->view, cx, 0);
724 if (tPtr->flags.secure) {
725 wfree(text);
729 static void drawRelief(WMView * view, Bool beveled)
731 WMScreen *scr = view->screen;
732 Display *dpy = scr->display;
733 GC wgc;
734 GC lgc;
735 GC dgc;
736 int width = view->size.width;
737 int height = view->size.height;
739 dgc = WMColorGC(scr->darkGray);
741 if (!beveled) {
742 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
744 return;
746 wgc = WMColorGC(scr->white);
747 lgc = WMColorGC(scr->gray);
749 /* top left */
750 XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
751 XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
753 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
754 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
756 /* bottom right */
757 XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
758 XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
760 XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
761 XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
764 static void paintTextField(TextField * tPtr)
766 W_Screen *screen = tPtr->view->screen;
767 W_View *view = tPtr->view;
768 W_View viewbuffer;
769 int tx, ty, tw;
770 int rx;
771 int bd;
772 int totalWidth;
773 char *text;
774 Pixmap drawbuffer;
775 WMColor *color;
777 if (!view->flags.realized || !view->flags.mapped)
778 return;
780 if (!tPtr->flags.bordered) {
781 bd = 0;
782 } else {
783 bd = 2;
786 if (tPtr->flags.secure) {
787 text = makeHiddenString(strlen(tPtr->text));
788 } else {
789 text = tPtr->text;
792 totalWidth = tPtr->view->size.width - 2 * bd;
794 drawbuffer = XCreatePixmap(screen->display, view->window,
795 view->size.width, view->size.height, screen->depth);
796 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
797 0, 0, view->size.width, view->size.height);
798 /* this is quite dirty */
799 viewbuffer.screen = view->screen;
800 viewbuffer.size = view->size;
801 viewbuffer.window = drawbuffer;
803 if (tPtr->textLen > 0) {
804 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
806 ty = tPtr->offsetWidth;
807 switch (tPtr->flags.alignment) {
808 case WALeft:
809 tx = tPtr->offsetWidth + 1;
810 if (tw < tPtr->usableWidth)
811 XFillRectangle(screen->display, drawbuffer,
812 WMColorGC(screen->white),
813 bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
814 break;
816 case WACenter:
817 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
818 if (tw < tPtr->usableWidth)
819 XClearArea(screen->display, view->window, bd, bd,
820 totalWidth, view->size.height - 2 * bd, False);
821 break;
823 default:
824 case WARight:
825 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
826 if (tw < tPtr->usableWidth)
827 XClearArea(screen->display, view->window, bd, bd,
828 totalWidth - tw, view->size.height - 2 * bd, False);
829 break;
832 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
834 WMDrawImageString(screen, drawbuffer, color, screen->white,
835 tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
836 tPtr->textLen - tPtr->viewPosition);
838 if (tPtr->selection.count) {
839 int count, count2;
841 count = tPtr->selection.count < 0
842 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
843 count2 = abs(tPtr->selection.count);
844 if (count < tPtr->viewPosition) {
845 count2 = abs(count2 - abs(tPtr->viewPosition - count));
846 count = tPtr->viewPosition;
849 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
850 - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
852 WMDrawImageString(screen, drawbuffer, color, screen->gray,
853 tPtr->font, rx, ty, &(text[count]), count2);
855 } else {
856 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
857 bd, bd, totalWidth, view->size.height - 2 * bd);
860 /* draw relief */
861 if (tPtr->flags.bordered) {
862 drawRelief(&viewbuffer, tPtr->flags.beveled);
865 if (tPtr->flags.secure)
866 wfree(text);
867 XCopyArea(screen->display, drawbuffer, view->window,
868 screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
869 XFreePixmap(screen->display, drawbuffer);
871 /* draw cursor */
872 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
873 paintCursor(tPtr);
877 static void blinkCursor(void *data)
879 TextField *tPtr = (TextField *) data;
881 if (tPtr->flags.cursorOn) {
882 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor, data);
883 } else {
884 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, data);
886 paintCursor(tPtr);
887 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
890 static void handleEvents(XEvent * event, void *data)
892 TextField *tPtr = (TextField *) data;
894 CHECK_CLASS(data, WC_TextField);
896 switch (event->type) {
897 case FocusIn:
898 W_FocusIC(tPtr->view);
899 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
900 return;
901 tPtr->flags.focused = 1;
903 if (!tPtr->timerID) {
904 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
907 paintTextField(tPtr);
909 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
911 tPtr->flags.notIllegalMovement = 0;
912 break;
914 case FocusOut:
915 W_UnFocusIC(tPtr->view);
916 tPtr->flags.focused = 0;
918 if (tPtr->timerID)
919 WMDeleteTimerHandler(tPtr->timerID);
920 tPtr->timerID = NULL;
922 paintTextField(tPtr);
923 if (!tPtr->flags.notIllegalMovement) {
924 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
925 (void *)WMIllegalTextMovement);
927 break;
929 case Expose:
930 if (event->xexpose.count != 0)
931 break;
932 paintTextField(tPtr);
933 break;
935 case DestroyNotify:
936 destroyTextField(tPtr);
937 break;
941 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
943 char buffer[64];
944 KeySym ksym;
945 const char *textEvent = NULL;
946 void *data = NULL;
947 int count, refresh = 0;
948 int control_pressed = 0;
949 int cancelSelection = 1;
950 Bool shifted, controled, modified;
951 Bool relay = True;
953 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
954 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
955 control_pressed = 1;
957 shifted = (event->xkey.state & ShiftMask ? True : False);
958 controled = (event->xkey.state & ControlMask ? True : False);
959 modified = shifted || controled;
961 count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
962 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
963 buffer[count] = '\0';
965 switch (ksym) {
966 case XK_Tab:
967 #ifdef XK_ISO_Left_Tab
968 /* FALLTHRU */
969 case XK_ISO_Left_Tab:
970 #endif
971 if (!controled) {
972 if (shifted) {
973 if (tPtr->view->prevFocusChain) {
974 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
975 tPtr->view->prevFocusChain);
976 tPtr->flags.notIllegalMovement = 1;
978 data = (void *)WMBacktabTextMovement;
979 } else {
980 if (tPtr->view->nextFocusChain) {
981 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
982 tPtr->view->nextFocusChain);
983 tPtr->flags.notIllegalMovement = 1;
985 data = (void *)WMTabTextMovement;
987 textEvent = WMTextDidEndEditingNotification;
989 cancelSelection = 0;
991 relay = False;
993 break;
995 case XK_Escape:
996 if (!modified) {
997 data = (void *)WMEscapeTextMovement;
998 textEvent = WMTextDidEndEditingNotification;
1000 relay = False;
1002 break;
1004 #ifdef XK_KP_Enter
1005 /* FALLTHRU */
1006 case XK_KP_Enter:
1007 #endif
1008 /* FALLTHRU */
1009 case XK_Return:
1010 if (!modified) {
1011 data = (void *)WMReturnTextMovement;
1012 textEvent = WMTextDidEndEditingNotification;
1014 relay = False;
1016 break;
1018 case WM_EMACSKEY_LEFT:
1019 if (!control_pressed)
1020 goto normal_key;
1021 else
1022 controled = False;
1024 #ifdef XK_KP_Left
1025 /* FALLTHRU */
1026 case XK_KP_Left:
1027 #endif
1028 /* FALLTHRU */
1029 case XK_Left:
1030 if (tPtr->cursorPosition > 0) {
1031 int i;
1032 paintCursor(tPtr);
1034 i = tPtr->cursorPosition;
1035 i += oneUTF8CharBackward(&tPtr->text[i], i);
1036 if (controled) {
1037 while (i > 0 && tPtr->text[i] != ' ')
1038 i--;
1039 while (i > 0 && tPtr->text[i] == ' ')
1040 i--;
1042 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1043 } else
1044 tPtr->cursorPosition = i;
1046 if (tPtr->cursorPosition < tPtr->viewPosition) {
1047 tPtr->viewPosition = tPtr->cursorPosition;
1048 refresh = 1;
1049 } else
1050 paintCursor(tPtr);
1052 if (shifted)
1053 cancelSelection = 0;
1055 relay = False;
1057 break;
1059 case WM_EMACSKEY_RIGHT:
1060 if (!control_pressed)
1061 goto normal_key;
1062 else
1063 controled = False;
1065 #ifdef XK_KP_Right
1066 /* FALLTHRU */
1067 case XK_KP_Right:
1068 #endif
1069 /* FALLTHRU */
1070 case XK_Right:
1071 if (tPtr->cursorPosition < tPtr->textLen) {
1072 int i;
1073 paintCursor(tPtr);
1075 i = tPtr->cursorPosition;
1076 if (controled) {
1077 while (tPtr->text[i] && tPtr->text[i] != ' ')
1078 i++;
1079 while (tPtr->text[i] == ' ')
1080 i++;
1081 } else {
1082 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1084 tPtr->cursorPosition = i;
1086 refresh = incrToFit2(tPtr);
1088 if (!refresh)
1089 paintCursor(tPtr);
1091 if (shifted)
1092 cancelSelection = 0;
1094 relay = False;
1096 break;
1098 case WM_EMACSKEY_HOME:
1099 if (!control_pressed)
1100 goto normal_key;
1101 else
1102 controled = False;
1104 #ifdef XK_KP_Home
1105 /* FALLTHRU */
1106 case XK_KP_Home:
1107 #endif
1108 /* FALLTHRU */
1109 case XK_Home:
1110 if (!controled) {
1111 if (tPtr->cursorPosition > 0) {
1112 paintCursor(tPtr);
1113 tPtr->cursorPosition = 0;
1114 if (tPtr->viewPosition > 0) {
1115 tPtr->viewPosition = 0;
1116 refresh = 1;
1117 } else
1118 paintCursor(tPtr);
1120 if (shifted)
1121 cancelSelection = 0;
1123 relay = False;
1125 break;
1127 case WM_EMACSKEY_END:
1128 if (!control_pressed)
1129 goto normal_key;
1130 else
1131 controled = False;
1133 #ifdef XK_KP_End
1134 /* FALLTHRU */
1135 case XK_KP_End:
1136 #endif
1137 /* FALLTHRU */
1138 case XK_End:
1139 if (!controled) {
1140 if (tPtr->cursorPosition < tPtr->textLen) {
1141 paintCursor(tPtr);
1142 tPtr->cursorPosition = tPtr->textLen;
1143 tPtr->viewPosition = 0;
1145 refresh = incrToFit(tPtr);
1147 if (!refresh)
1148 paintCursor(tPtr);
1150 if (shifted)
1151 cancelSelection = 0;
1153 relay = False;
1155 break;
1157 case WM_EMACSKEY_BS:
1158 if (!control_pressed)
1159 goto normal_key;
1160 else
1161 modified = False;
1163 /* FALLTHRU */
1164 case XK_BackSpace:
1165 if (!modified) {
1166 if (tPtr->selection.count) {
1167 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1168 data = (void *)WMDeleteTextEvent;
1169 textEvent = WMTextDidChangeNotification;
1170 } else if (tPtr->cursorPosition > 0) {
1171 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1172 tPtr->cursorPosition);
1173 WMRange range;
1174 range.position = tPtr->cursorPosition + i;
1175 range.count = -i;
1176 WMDeleteTextFieldRange(tPtr, range);
1177 data = (void *)WMDeleteTextEvent;
1178 textEvent = WMTextDidChangeNotification;
1181 relay = False;
1183 break;
1185 case WM_EMACSKEY_DEL:
1186 if (!control_pressed)
1187 goto normal_key;
1188 else
1189 modified = False;
1191 #ifdef XK_KP_Delete
1192 /* FALLTHRU */
1193 case XK_KP_Delete:
1194 #endif
1195 /* FALLTHRU */
1196 case XK_Delete:
1197 if (!modified) {
1198 if (tPtr->selection.count) {
1199 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1200 data = (void *)WMDeleteTextEvent;
1201 textEvent = WMTextDidChangeNotification;
1202 } else if (tPtr->cursorPosition < tPtr->textLen) {
1203 WMRange range;
1204 range.position = tPtr->cursorPosition;
1205 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1206 tPtr->textLen - tPtr->cursorPosition);
1207 WMDeleteTextFieldRange(tPtr, range);
1208 data = (void *)WMDeleteTextEvent;
1209 textEvent = WMTextDidChangeNotification;
1212 relay = False;
1214 break;
1216 normal_key:
1217 default:
1218 if (!controled) {
1219 if (count > 0 && !iscntrl(buffer[0])) {
1220 if (tPtr->selection.count)
1221 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1222 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1223 data = (void *)WMInsertTextEvent;
1224 textEvent = WMTextDidChangeNotification;
1226 relay = False;
1229 break;
1232 if (relay) {
1233 WMRelayToNextResponder(W_VIEW(tPtr), event);
1234 return;
1237 /* Do not allow text selection in secure text fields */
1238 if (cancelSelection || tPtr->flags.secure) {
1239 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1241 if (tPtr->selection.count) {
1242 tPtr->selection.count = 0;
1243 refresh = 1;
1245 tPtr->selection.position = tPtr->cursorPosition;
1246 } else {
1247 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1249 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1251 refresh = 1;
1255 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1257 if (textEvent) {
1258 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1260 if (tPtr->delegate) {
1261 if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1262 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1264 else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1265 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1267 else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1268 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1271 WMPostNotification(notif);
1272 WMReleaseNotification(notif);
1275 if (refresh)
1276 paintTextField(tPtr);
1278 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1281 static int pointToCursorPosition(TextField * tPtr, int x)
1283 int a, b, pos, prev, tw;
1285 if (tPtr->flags.bordered)
1286 x -= 2;
1288 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1289 tPtr->textLen - tPtr->viewPosition) <= x)
1290 return tPtr->textLen;
1292 a = tPtr->viewPosition;
1293 b = tPtr->textLen;
1295 /* we halve the text until we get into a 10 byte vicinity of x */
1296 while (b - a > 10) {
1297 pos = (a + b) / 2;
1298 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1299 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1300 if (tw > x) {
1301 b = pos;
1302 } else if (tw < x) {
1303 a = pos;
1304 } else {
1305 return pos;
1309 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1310 * inclusive, with the exception of the left border of the 'a' glyph and
1311 * the right border or the 'b-1' glyph
1313 * ( <--- range for x's position ---> )
1314 * a a+1 .......................... b-1 b
1316 pos = prev = a;
1317 while (pos <= b) {
1318 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1319 if (tw > x) {
1320 return prev;
1321 } else if (pos == b) {
1322 break;
1324 prev = pos;
1325 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1328 return b;
1331 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1333 TextField *tPtr = (TextField *) view->self;
1334 char *str;
1336 /* Parameter not used, but tell the compiler that it is ok */
1337 (void) selection;
1338 (void) target;
1339 (void) timestamp;
1340 (void) cdata;
1342 tPtr->flags.waitingSelection = 0;
1344 if (data != NULL) {
1345 str = (char *)WMDataBytes(data);
1347 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1348 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1349 } else {
1350 int n;
1352 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1354 if (str != NULL) {
1355 str[n] = 0;
1356 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1357 XFree(str);
1358 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1363 static void handleTextFieldActionEvents(XEvent * event, void *data)
1365 TextField *tPtr = (TextField *) data;
1366 static Time lastButtonReleasedEvent = 0;
1367 static Time lastButtonReleasedEvent2 = 0;
1368 Display *dpy = event->xany.display;
1370 CHECK_CLASS(data, WC_TextField);
1372 switch (event->type) {
1373 case KeyPress:
1374 if (tPtr->flags.waitingSelection) {
1375 return;
1377 if (tPtr->flags.enabled && tPtr->flags.focused) {
1378 handleTextFieldKeyPress(tPtr, event);
1379 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1380 tPtr->flags.pointerGrabbed = 1;
1382 break;
1384 case MotionNotify:
1386 if (tPtr->flags.pointerGrabbed) {
1387 tPtr->flags.pointerGrabbed = 0;
1388 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1390 if (tPtr->flags.waitingSelection) {
1391 return;
1394 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1396 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1397 if (WMWidthOfString(tPtr->font,
1398 &(tPtr->text[tPtr->viewPosition]),
1399 tPtr->cursorPosition - tPtr->viewPosition)
1400 > tPtr->usableWidth) {
1401 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1402 tPtr->textLen -
1403 tPtr->viewPosition);
1405 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1406 paintCursor(tPtr);
1407 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1408 tPtr->viewPosition);
1411 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1413 /* Do not allow text selection in secure textfields */
1414 if (tPtr->flags.secure) {
1415 tPtr->selection.position = tPtr->cursorPosition;
1418 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1420 paintCursor(tPtr);
1421 paintTextField(tPtr);
1424 break;
1426 case ButtonPress:
1427 if (tPtr->flags.pointerGrabbed) {
1428 tPtr->flags.pointerGrabbed = 0;
1429 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1430 break;
1433 if (tPtr->flags.waitingSelection) {
1434 break;
1437 switch (tPtr->flags.alignment) {
1438 int textWidth;
1439 case WARight:
1440 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1441 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1442 WMSetFocusToWidget(tPtr);
1444 if (tPtr->flags.focused) {
1445 tPtr->selection.position = tPtr->cursorPosition;
1446 tPtr->selection.count = 0;
1448 if (textWidth < tPtr->usableWidth) {
1449 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1450 event->xbutton.x - tPtr->usableWidth
1451 + textWidth);
1452 } else
1453 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1455 paintTextField(tPtr);
1456 break;
1458 case WALeft:
1459 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1460 WMSetFocusToWidget(tPtr);
1462 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1463 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1464 tPtr->selection.position = tPtr->cursorPosition;
1465 tPtr->selection.count = 0;
1466 paintTextField(tPtr);
1468 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1469 char *text;
1470 int n;
1472 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1473 event->xbutton.time, pasteText, NULL)) {
1474 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1476 if (text) {
1477 text[n] = 0;
1478 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1479 XFree(text);
1480 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1481 (void *)WMInsertTextEvent);
1483 } else {
1484 tPtr->flags.waitingSelection = 1;
1487 break;
1488 default:
1489 break;
1491 break;
1493 case ButtonRelease:
1494 if (tPtr->flags.pointerGrabbed) {
1495 tPtr->flags.pointerGrabbed = 0;
1496 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1498 if (tPtr->flags.waitingSelection) {
1499 break;
1502 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1503 int start, count;
1504 XRotateBuffers(dpy, 1);
1506 count = abs(tPtr->selection.count);
1507 if (tPtr->selection.count < 0)
1508 start = tPtr->selection.position - count;
1509 else
1510 start = tPtr->selection.position;
1512 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1515 if (!tPtr->flags.secure &&
1516 event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1518 if (event->xbutton.time - lastButtonReleasedEvent2 <=
1519 2 * WINGsConfiguration.doubleClickDelay) {
1520 tPtr->selection.position = 0;
1521 tPtr->selection.count = tPtr->textLen;
1522 } else {
1523 int pos, cnt;
1524 char *txt;
1525 pos = tPtr->selection.position;
1526 cnt = tPtr->selection.count;
1527 txt = tPtr->text;
1528 while (pos >= 0) {
1529 if (txt[pos] == ' ' || txt[pos] == '\t')
1530 break;
1531 pos--;
1533 pos++;
1535 while (pos + cnt < tPtr->textLen) {
1536 if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1537 break;
1538 cnt++;
1540 tPtr->selection.position = pos;
1541 tPtr->selection.count = cnt;
1543 paintTextField(tPtr);
1545 if (!tPtr->flags.ownsSelection) {
1546 tPtr->flags.ownsSelection =
1547 WMCreateSelectionHandler(tPtr->view,
1548 XA_PRIMARY,
1549 event->xbutton.time, &selectionHandler, NULL);
1551 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1552 tPtr->flags.ownsSelection =
1553 WMCreateSelectionHandler(tPtr->view,
1554 XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1557 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1558 lastButtonReleasedEvent = event->xbutton.time;
1560 break;
1564 static void destroyTextField(TextField * tPtr)
1566 if (tPtr->timerID)
1567 WMDeleteTimerHandler(tPtr->timerID);
1569 W_DestroyIC(tPtr->view);
1571 WMReleaseFont(tPtr->font);
1572 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1573 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1574 WMRemoveNotificationObserver(tPtr);
1576 if (tPtr->text)
1577 wfree(tPtr->text);
1579 wfree(tPtr);