WINGs: Bounded string ops
[wmaker-crm.git] / WINGs / wtextfield.c
blob67cc230d518149a09cc4f6ef9b61bdb458a121d2
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 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
14 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
15 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 #if 0
44 WMHandlerID timerID; /* for cursor blinking */
45 #endif
46 struct {
47 WMAlignment alignment:2;
49 unsigned int bordered:1;
51 unsigned int beveled:1;
53 unsigned int enabled:1;
55 unsigned int focused:1;
57 unsigned int cursorOn:1;
59 unsigned int secure:1; /* password entry style */
61 unsigned int pointerGrabbed:1;
63 unsigned int ownsSelection:1;
65 unsigned int waitingSelection:1; /* requested selection, but
66 * didnt get yet */
68 unsigned int notIllegalMovement:1;
69 } flags;
70 } TextField;
72 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
73 if ((T)->delegate && (T)->delegate->C)\
74 (*(T)->delegate->C)((T)->delegate,notif);\
75 WMPostNotification(notif);\
76 WMReleaseNotification(notif);}
78 #define MIN_TEXT_BUFFER 2
79 #define TEXT_BUFFER_INCR 8
81 #define WM_EMACSKEYMASK ControlMask
83 #define WM_EMACSKEY_LEFT XK_b
84 #define WM_EMACSKEY_RIGHT XK_f
85 #define WM_EMACSKEY_HOME XK_a
86 #define WM_EMACSKEY_END XK_e
87 #define WM_EMACSKEY_BS XK_h
88 #define WM_EMACSKEY_DEL XK_d
90 #define DEFAULT_WIDTH 60
91 #define DEFAULT_HEIGHT 20
92 #define DEFAULT_BORDERED True
93 #define DEFAULT_ALIGNMENT WALeft
95 static void destroyTextField(TextField * tPtr);
96 static void paintTextField(TextField * tPtr);
98 static void handleEvents(XEvent * event, void *data);
99 static void handleTextFieldActionEvents(XEvent * event, void *data);
100 static void didResizeTextField();
102 struct W_ViewDelegate _TextFieldViewDelegate = {
103 NULL,
104 NULL,
105 didResizeTextField,
106 NULL,
107 NULL
110 static void lostSelection(WMView * view, Atom selection, void *cdata);
112 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type);
114 static WMSelectionProcs selectionHandler = {
115 requestHandler,
116 lostSelection,
117 NULL
120 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
121 &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
123 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
124 &((tPtr)->text[(start)]), (end) - (start)))
126 static INLINE int oneUTF8CharBackward(char *str, int len)
128 unsigned char *ustr = (unsigned char *)str;
129 int pos = 0;
131 while (len-- > 0 && ustr[--pos] >= 0x80 && ustr[pos] <= 0xbf) ;
132 return pos;
135 static INLINE int oneUTF8CharForward(char *str, int len)
137 unsigned char *ustr = (unsigned char *)str;
138 int pos = 0;
140 while (len-- > 0 && ustr[++pos] >= 0x80 && ustr[pos] <= 0xbf) ;
141 return pos;
144 // find the beginning of the UTF8 char pointed by str
145 static INLINE int seekUTF8CharStart(char *str, int len)
147 unsigned char *ustr = (unsigned char *)str;
148 int pos = 0;
150 while (len-- > 0 && ustr[pos] >= 0x80 && ustr[pos] <= 0xbf)
151 --pos;
152 return pos;
155 static void normalizeRange(TextField * tPtr, WMRange * range)
157 if (range->position < 0 && range->count < 0)
158 range->count = 0;
160 if (range->count == 0) {
161 /*range->position = 0; why is this? */
162 return;
165 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
166 if (range->count < 0) { /* && range->position >= 0 */
167 if (range->position + range->count < 0) {
168 range->count = range->position;
169 range->position = 0;
170 } else {
171 range->count = -range->count;
172 range->position -= range->count;
174 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
175 } else if (range->position < 0) { /* && range->count > 0 */
176 if (range->position + range->count < 0) {
177 range->position = range->count = 0;
178 } else {
179 range->count += range->position;
180 range->position = 0;
184 if (range->position + range->count > tPtr->textLen)
185 range->count = tPtr->textLen - range->position;
188 static void memmv(char *dest, char *src, int size)
190 int i;
192 if (dest > src) {
193 for (i = size - 1; i >= 0; i--) {
194 dest[i] = src[i];
196 } else if (dest < src) {
197 for (i = 0; i < size; i++) {
198 dest[i] = src[i];
203 static int incrToFit(TextField * tPtr)
205 int vp = tPtr->viewPosition;
207 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
208 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
209 tPtr->textLen - tPtr->viewPosition);
211 return vp != tPtr->viewPosition;
214 static int incrToFit2(TextField * tPtr)
216 int vp = tPtr->viewPosition;
218 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
219 >= tPtr->usableWidth)
220 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
221 tPtr->cursorPosition - tPtr->viewPosition);
222 return vp != tPtr->viewPosition;
225 static void decrToFit(TextField * tPtr)
227 int vp = tPtr->viewPosition;
229 while (vp > 0 && (vp += oneUTF8CharBackward(&tPtr->text[vp], vp),
230 TEXT_WIDTH(tPtr, vp)) < tPtr->usableWidth) {
231 tPtr->viewPosition = vp;
235 #undef TEXT_WIDTH
236 #undef TEXT_WIDTH2
238 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type)
240 TextField *tPtr = view->self;
241 int count;
242 Display *dpy = tPtr->view->screen->display;
243 Atom _TARGETS;
244 Atom TEXT = XInternAtom(dpy, "TEXT", False);
245 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
246 WMData *data;
248 count = tPtr->selection.count < 0
249 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
251 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
253 data = WMCreateDataWithBytes(&(tPtr->text[count]), abs(tPtr->selection.count));
254 WMSetDataFormat(data, 8);
255 *type = target;
257 return data;
260 _TARGETS = XInternAtom(dpy, "TARGETS", False);
261 if (target == _TARGETS) {
262 Atom *ptr;
264 ptr = wmalloc(4 * sizeof(Atom));
265 ptr[0] = _TARGETS;
266 ptr[1] = XA_STRING;
267 ptr[2] = TEXT;
268 ptr[3] = COMPOUND_TEXT;
270 data = WMCreateDataWithBytes(ptr, 4 * 4);
271 WMSetDataFormat(data, 32);
273 *type = target;
274 return data;
277 return NULL;
281 static void lostSelection(WMView * view, Atom selection, void *cdata)
283 TextField *tPtr = (WMTextField *) view->self;
285 if (tPtr->flags.ownsSelection) {
286 WMDeleteSelectionHandler(view, selection, CurrentTime);
287 tPtr->flags.ownsSelection = 0;
289 if (tPtr->selection.count != 0) {
290 tPtr->selection.count = 0;
291 paintTextField(tPtr);
295 static void selectionNotification(void *observerData, WMNotification * notification)
297 WMView *observerView = (WMView *) observerData;
298 WMView *newOwnerView = (WMView *) WMGetNotificationClientData(notification);
300 if (observerView != newOwnerView) {
302 //if (tPtr->flags.ownsSelection)
303 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
305 lostSelection(observerView, XA_PRIMARY, NULL);
309 static void realizeObserver(void *self, WMNotification * not)
311 W_CreateIC(((TextField *) self)->view);
314 WMTextField *WMCreateTextField(WMWidget * parent)
316 TextField *tPtr;
318 tPtr = wmalloc(sizeof(TextField));
319 tPtr->widgetClass = WC_TextField;
321 tPtr->view = W_CreateView(W_VIEW(parent));
322 if (!tPtr->view) {
323 wfree(tPtr);
324 return NULL;
326 tPtr->view->self = tPtr;
328 tPtr->view->delegate = &_TextFieldViewDelegate;
330 tPtr->view->attribFlags |= CWCursor;
331 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
333 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
335 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
336 tPtr->textLen = 0;
337 tPtr->bufferSize = MIN_TEXT_BUFFER;
339 tPtr->flags.enabled = 1;
341 WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask | FocusChangeMask, handleEvents, tPtr);
343 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
345 tPtr->flags.bordered = DEFAULT_BORDERED;
346 tPtr->flags.beveled = True;
347 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
348 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
350 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
352 WMCreateEventHandler(tPtr->view, EnterWindowMask | LeaveWindowMask
353 | ButtonReleaseMask | ButtonPressMask | KeyPressMask | Button1MotionMask,
354 handleTextFieldActionEvents, tPtr);
356 WMAddNotificationObserver(selectionNotification, tPtr->view,
357 WMSelectionOwnerDidChangeNotification, (void *)XA_PRIMARY);
359 WMAddNotificationObserver(realizeObserver, tPtr, WMViewRealizedNotification, tPtr->view);
361 tPtr->flags.cursorOn = 1;
363 return tPtr;
366 void WMSetTextFieldDelegate(WMTextField * tPtr, WMTextFieldDelegate * delegate)
368 CHECK_CLASS(tPtr, WC_TextField);
370 tPtr->delegate = delegate;
373 WMTextFieldDelegate *WMGetTextFieldDelegate(WMTextField * tPtr)
375 CHECK_CLASS(tPtr, WC_TextField);
377 return tPtr->delegate;
380 void WMInsertTextFieldText(WMTextField * tPtr, char *text, int position)
382 int len;
384 CHECK_CLASS(tPtr, WC_TextField);
386 if (!text)
387 return;
389 len = strlen(text);
391 /* check if buffer will hold the text */
392 if (len + tPtr->textLen >= tPtr->bufferSize) {
393 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
394 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
397 if (position < 0 || position >= tPtr->textLen) {
398 /* append the text at the end */
399 wstrlcat(tPtr->text, text, tPtr->bufferSize);
400 tPtr->textLen += len;
401 tPtr->cursorPosition += len;
402 incrToFit(tPtr);
403 } else {
404 /* insert text at position */
405 memmv(&(tPtr->text[position + len]), &(tPtr->text[position]), tPtr->textLen - position + 1);
407 memcpy(&(tPtr->text[position]), text, len);
409 tPtr->textLen += len;
410 if (position >= tPtr->cursorPosition) {
411 tPtr->cursorPosition += len;
412 incrToFit2(tPtr);
413 } else {
414 incrToFit(tPtr);
418 paintTextField(tPtr);
421 void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
423 CHECK_CLASS(tPtr, WC_TextField);
425 normalizeRange(tPtr, &range);
427 if (!range.count)
428 return;
430 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position + range.count]),
431 tPtr->textLen - (range.position + range.count) + 1);
433 /* better than nothing ;) */
434 if (tPtr->cursorPosition > range.position)
435 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition], tPtr->viewPosition);
436 tPtr->textLen -= range.count;
437 tPtr->cursorPosition = range.position;
439 decrToFit(tPtr);
441 paintTextField(tPtr);
444 char *WMGetTextFieldText(WMTextField * tPtr)
446 CHECK_CLASS(tPtr, WC_TextField);
448 return wstrdup(tPtr->text);
451 void WMSetTextFieldText(WMTextField * tPtr, char *text)
453 CHECK_CLASS(tPtr, WC_TextField);
455 if ((text && strcmp(tPtr->text, text) == 0) || (!text && tPtr->textLen == 0))
456 return;
458 if (text == NULL) {
459 tPtr->text[0] = 0;
460 tPtr->textLen = 0;
461 } else {
462 tPtr->textLen = strlen(text);
464 if (tPtr->textLen >= tPtr->bufferSize) {
465 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
466 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
468 wstrlcpy(tPtr->text, text, tPtr->bufferSize);
471 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
472 tPtr->viewPosition = 0;
473 tPtr->selection.count = 0;
475 if (tPtr->view->flags.realized)
476 paintTextField(tPtr);
479 void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
481 CHECK_CLASS(tPtr, WC_TextField);
483 tPtr->flags.alignment = alignment;
485 if (alignment != WALeft) {
486 wwarning("only left alignment is supported in textfields");
487 return;
490 if (tPtr->view->flags.realized) {
491 paintTextField(tPtr);
495 void WMSetTextFieldBordered(WMTextField * tPtr, Bool bordered)
497 CHECK_CLASS(tPtr, WC_TextField);
499 tPtr->flags.bordered = bordered;
501 if (tPtr->view->flags.realized) {
502 paintTextField(tPtr);
506 void WMSetTextFieldBeveled(WMTextField * tPtr, Bool flag)
508 CHECK_CLASS(tPtr, WC_TextField);
510 tPtr->flags.beveled = ((flag == 0) ? 0 : 1);
512 if (tPtr->view->flags.realized) {
513 paintTextField(tPtr);
517 void WMSetTextFieldSecure(WMTextField * tPtr, Bool flag)
519 CHECK_CLASS(tPtr, WC_TextField);
521 tPtr->flags.secure = ((flag == 0) ? 0 : 1);
523 if (tPtr->view->flags.realized) {
524 paintTextField(tPtr);
528 Bool WMGetTextFieldEditable(WMTextField * tPtr)
530 CHECK_CLASS(tPtr, WC_TextField);
532 return tPtr->flags.enabled;
535 void WMSetTextFieldEditable(WMTextField * tPtr, Bool flag)
537 CHECK_CLASS(tPtr, WC_TextField);
539 tPtr->flags.enabled = ((flag == 0) ? 0 : 1);
541 if (tPtr->view->flags.realized) {
542 paintTextField(tPtr);
546 void WMSelectTextFieldRange(WMTextField * tPtr, WMRange range)
548 CHECK_CLASS(tPtr, WC_TextField);
550 if (tPtr->flags.enabled) {
551 normalizeRange(tPtr, &range);
553 tPtr->selection = range;
555 tPtr->cursorPosition = range.position + range.count;
557 if (tPtr->view->flags.realized) {
558 paintTextField(tPtr);
563 void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position)
565 CHECK_CLASS(tPtr, WC_TextField);
567 if (tPtr->flags.enabled) {
568 if (position > tPtr->textLen)
569 position = tPtr->textLen;
571 tPtr->cursorPosition = position;
572 if (tPtr->view->flags.realized) {
573 paintTextField(tPtr);
578 unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr)
580 CHECK_CLASS(tPtr, WC_TextField);
582 return tPtr->cursorPosition;
585 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
587 CHECK_CLASS(tPtr, WC_TextField);
588 if (next == NULL) {
589 if (tPtr->view->nextFocusChain)
590 tPtr->view->nextFocusChain->prevFocusChain = NULL;
591 tPtr->view->nextFocusChain = NULL;
592 return;
595 CHECK_CLASS(next, WC_TextField);
597 if (tPtr->view->nextFocusChain)
598 tPtr->view->nextFocusChain->prevFocusChain = NULL;
599 if (next->view->prevFocusChain)
600 next->view->prevFocusChain->nextFocusChain = NULL;
602 tPtr->view->nextFocusChain = next->view;
603 next->view->prevFocusChain = tPtr->view;
606 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
608 CHECK_CLASS(tPtr, WC_TextField);
609 if (prev == NULL) {
610 if (tPtr->view->prevFocusChain)
611 tPtr->view->prevFocusChain->nextFocusChain = NULL;
612 tPtr->view->prevFocusChain = NULL;
613 return;
616 CHECK_CLASS(prev, WC_TextField);
618 if (tPtr->view->prevFocusChain)
619 tPtr->view->prevFocusChain->nextFocusChain = NULL;
620 if (prev->view->nextFocusChain)
621 prev->view->nextFocusChain->prevFocusChain = NULL;
623 tPtr->view->prevFocusChain = prev->view;
624 prev->view->nextFocusChain = tPtr->view;
627 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
629 CHECK_CLASS(tPtr, WC_TextField);
631 if (tPtr->font)
632 WMReleaseFont(tPtr->font);
633 tPtr->font = WMRetainFont(font);
635 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
637 if (tPtr->view->flags.realized) {
638 paintTextField(tPtr);
642 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
644 return tPtr->font;
647 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
649 WMTextField *tPtr = (WMTextField *) view->self;
651 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
653 tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth /*+ 2 */ ;
656 static char *makeHiddenString(int length)
658 char *data = wmalloc(length + 1);
660 memset(data, '*', length);
661 data[length] = '\0';
663 return data;
666 static void paintCursor(TextField * tPtr)
668 int cx;
669 WMScreen *screen = tPtr->view->screen;
670 int textWidth;
671 char *text;
673 if (tPtr->flags.secure)
674 text = makeHiddenString(strlen(tPtr->text));
675 else
676 text = tPtr->text;
678 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
680 switch (tPtr->flags.alignment) {
681 case WARight:
682 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
683 if (textWidth < tPtr->usableWidth)
684 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
685 else
686 cx += tPtr->offsetWidth + 1;
687 break;
688 case WALeft:
689 cx += tPtr->offsetWidth + 1;
690 break;
691 case WAJustified:
692 /* not supported */
693 case WACenter:
694 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
695 if (textWidth < tPtr->usableWidth)
696 cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
697 else
698 cx += tPtr->offsetWidth;
699 break;
702 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
703 cx, tPtr->offsetWidth, 1,
704 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
705 printf("%d %d\n",cx,tPtr->cursorPosition);
708 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
709 cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
711 W_SetPreeditPositon(tPtr->view, cx, 0);
713 if (tPtr->flags.secure) {
714 wfree(text);
718 static void drawRelief(WMView * view, Bool beveled)
720 WMScreen *scr = view->screen;
721 Display *dpy = scr->display;
722 GC wgc;
723 GC lgc;
724 GC dgc;
725 int width = view->size.width;
726 int height = view->size.height;
728 dgc = WMColorGC(scr->darkGray);
730 if (!beveled) {
731 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
733 return;
735 wgc = WMColorGC(scr->white);
736 lgc = WMColorGC(scr->gray);
738 /* top left */
739 XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
740 XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
742 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
743 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
745 /* bottom right */
746 XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
747 XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
749 XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
750 XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
753 static void paintTextField(TextField * tPtr)
755 W_Screen *screen = tPtr->view->screen;
756 W_View *view = tPtr->view;
757 W_View viewbuffer;
758 int tx, ty, tw, th;
759 int rx;
760 int bd;
761 int totalWidth;
762 char *text;
763 Pixmap drawbuffer;
764 WMColor *color;
766 if (!view->flags.realized || !view->flags.mapped)
767 return;
769 if (!tPtr->flags.bordered) {
770 bd = 0;
771 } else {
772 bd = 2;
775 if (tPtr->flags.secure) {
776 text = makeHiddenString(strlen(tPtr->text));
777 } else {
778 text = tPtr->text;
781 totalWidth = tPtr->view->size.width - 2 * bd;
783 drawbuffer = XCreatePixmap(screen->display, view->window,
784 view->size.width, view->size.height, screen->depth);
785 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
786 0, 0, view->size.width, view->size.height);
787 /* this is quite dirty */
788 viewbuffer.screen = view->screen;
789 viewbuffer.size = view->size;
790 viewbuffer.window = drawbuffer;
792 if (tPtr->textLen > 0) {
793 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
795 th = WMFontHeight(tPtr->font);
797 ty = tPtr->offsetWidth;
798 switch (tPtr->flags.alignment) {
799 case WALeft:
800 tx = tPtr->offsetWidth + 1;
801 if (tw < tPtr->usableWidth)
802 XFillRectangle(screen->display, drawbuffer,
803 WMColorGC(screen->white),
804 bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
805 break;
807 case WACenter:
808 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
809 if (tw < tPtr->usableWidth)
810 XClearArea(screen->display, view->window, bd, bd,
811 totalWidth, view->size.height - 2 * bd, False);
812 break;
814 default:
815 case WARight:
816 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
817 if (tw < tPtr->usableWidth)
818 XClearArea(screen->display, view->window, bd, bd,
819 totalWidth - tw, view->size.height - 2 * bd, False);
820 break;
823 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
825 WMDrawImageString(screen, drawbuffer, color, screen->white,
826 tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
827 tPtr->textLen - tPtr->viewPosition);
829 if (tPtr->selection.count) {
830 int count, count2;
832 count = tPtr->selection.count < 0
833 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
834 count2 = abs(tPtr->selection.count);
835 if (count < tPtr->viewPosition) {
836 count2 = abs(count2 - abs(tPtr->viewPosition - count));
837 count = tPtr->viewPosition;
840 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
841 - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
843 WMDrawImageString(screen, drawbuffer, color, screen->gray,
844 tPtr->font, rx, ty, &(text[count]), count2);
846 } else {
847 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
848 bd, bd, totalWidth, view->size.height - 2 * bd);
851 /* draw relief */
852 if (tPtr->flags.bordered) {
853 drawRelief(&viewbuffer, tPtr->flags.beveled);
856 if (tPtr->flags.secure)
857 wfree(text);
858 XCopyArea(screen->display, drawbuffer, view->window,
859 screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
860 XFreePixmap(screen->display, drawbuffer);
862 /* draw cursor */
863 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
864 paintCursor(tPtr);
868 #if 0
869 static void blinkCursor(void *data)
871 TextField *tPtr = (TextField *) data;
873 if (tPtr->flags.cursorOn) {
874 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor, data);
875 } else {
876 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, data);
878 paintCursor(tPtr);
879 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
881 #endif
883 static void handleEvents(XEvent * event, void *data)
885 TextField *tPtr = (TextField *) data;
887 CHECK_CLASS(data, WC_TextField);
889 switch (event->type) {
890 case FocusIn:
891 W_FocusIC(tPtr->view);
892 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
893 return;
894 tPtr->flags.focused = 1;
895 #if 0
896 if (!tPtr->timerID) {
897 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
899 #endif
900 paintTextField(tPtr);
902 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
904 tPtr->flags.notIllegalMovement = 0;
905 break;
907 case FocusOut:
908 W_UnFocusIC(tPtr->view);
909 tPtr->flags.focused = 0;
910 #if 0
911 if (tPtr->timerID)
912 WMDeleteTimerHandler(tPtr->timerID);
913 tPtr->timerID = NULL;
914 #endif
916 paintTextField(tPtr);
917 if (!tPtr->flags.notIllegalMovement) {
918 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
919 (void *)WMIllegalTextMovement);
921 break;
923 case Expose:
924 if (event->xexpose.count != 0)
925 break;
926 paintTextField(tPtr);
927 break;
929 case DestroyNotify:
930 destroyTextField(tPtr);
931 break;
935 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
937 char buffer[64];
938 KeySym ksym;
939 char *textEvent = NULL;
940 void *data = NULL;
941 int count, refresh = 0;
942 int control_pressed = 0;
943 int cancelSelection = 1;
944 Bool shifted, controled, modified;
945 Bool relay = True;
947 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
948 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
949 control_pressed = 1;
951 shifted = (event->xkey.state & ShiftMask ? True : False);
952 controled = (event->xkey.state & ControlMask ? True : False);
953 modified = shifted || controled;
955 count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
956 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
957 buffer[count] = '\0';
959 switch (ksym) {
960 case XK_Tab:
961 #ifdef XK_ISO_Left_Tab
962 case XK_ISO_Left_Tab:
963 #endif
964 if (!controled) {
965 if (shifted) {
966 if (tPtr->view->prevFocusChain) {
967 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
968 tPtr->view->prevFocusChain);
969 tPtr->flags.notIllegalMovement = 1;
971 data = (void *)WMBacktabTextMovement;
972 } else {
973 if (tPtr->view->nextFocusChain) {
974 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
975 tPtr->view->nextFocusChain);
976 tPtr->flags.notIllegalMovement = 1;
978 data = (void *)WMTabTextMovement;
980 textEvent = WMTextDidEndEditingNotification;
982 cancelSelection = 0;
984 relay = False;
986 break;
988 case XK_Escape:
989 if (!modified) {
990 data = (void *)WMEscapeTextMovement;
991 textEvent = WMTextDidEndEditingNotification;
993 relay = False;
995 break;
997 case XK_Return:
998 if (!modified) {
999 data = (void *)WMReturnTextMovement;
1000 textEvent = WMTextDidEndEditingNotification;
1002 relay = False;
1004 break;
1006 case WM_EMACSKEY_LEFT:
1007 if (!control_pressed)
1008 goto normal_key;
1009 else
1010 controled = False;
1012 #ifdef XK_KP_Left
1013 case XK_KP_Left:
1014 #endif
1015 case XK_Left:
1016 if (tPtr->cursorPosition > 0) {
1017 int i;
1018 paintCursor(tPtr);
1020 i = tPtr->cursorPosition;
1021 i += oneUTF8CharBackward(&tPtr->text[i], i);
1022 if (controled) {
1023 while (i > 0 && tPtr->text[i] != ' ')
1024 i--;
1025 while (i > 0 && tPtr->text[i] == ' ')
1026 i--;
1028 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1029 } else
1030 tPtr->cursorPosition = i;
1032 if (tPtr->cursorPosition < tPtr->viewPosition) {
1033 tPtr->viewPosition = tPtr->cursorPosition;
1034 refresh = 1;
1035 } else
1036 paintCursor(tPtr);
1038 if (shifted)
1039 cancelSelection = 0;
1041 relay = False;
1043 break;
1045 case WM_EMACSKEY_RIGHT:
1046 if (!control_pressed)
1047 goto normal_key;
1048 else
1049 controled = False;
1051 #ifdef XK_KP_Right
1052 case XK_KP_Right:
1053 #endif
1054 case XK_Right:
1055 if (tPtr->cursorPosition < tPtr->textLen) {
1056 int i;
1057 paintCursor(tPtr);
1059 i = tPtr->cursorPosition;
1060 if (controled) {
1061 while (tPtr->text[i] && tPtr->text[i] != ' ')
1062 i++;
1063 while (tPtr->text[i] == ' ')
1064 i++;
1065 } else {
1066 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1068 tPtr->cursorPosition = i;
1070 refresh = incrToFit2(tPtr);
1072 if (!refresh)
1073 paintCursor(tPtr);
1075 if (shifted)
1076 cancelSelection = 0;
1078 relay = False;
1080 break;
1082 case WM_EMACSKEY_HOME:
1083 if (!control_pressed)
1084 goto normal_key;
1085 else
1086 controled = False;
1088 #ifdef XK_KP_Home
1089 case XK_KP_Home:
1090 #endif
1091 case XK_Home:
1092 if (!controled) {
1093 if (tPtr->cursorPosition > 0) {
1094 paintCursor(tPtr);
1095 tPtr->cursorPosition = 0;
1096 if (tPtr->viewPosition > 0) {
1097 tPtr->viewPosition = 0;
1098 refresh = 1;
1099 } else
1100 paintCursor(tPtr);
1102 if (shifted)
1103 cancelSelection = 0;
1105 relay = False;
1107 break;
1109 case WM_EMACSKEY_END:
1110 if (!control_pressed)
1111 goto normal_key;
1112 else
1113 controled = False;
1115 #ifdef XK_KP_End
1116 case XK_KP_End:
1117 #endif
1118 case XK_End:
1119 if (!controled) {
1120 if (tPtr->cursorPosition < tPtr->textLen) {
1121 paintCursor(tPtr);
1122 tPtr->cursorPosition = tPtr->textLen;
1123 tPtr->viewPosition = 0;
1125 refresh = incrToFit(tPtr);
1127 if (!refresh)
1128 paintCursor(tPtr);
1130 if (shifted)
1131 cancelSelection = 0;
1133 relay = False;
1135 break;
1137 case WM_EMACSKEY_BS:
1138 if (!control_pressed)
1139 goto normal_key;
1140 else
1141 modified = False;
1143 case XK_BackSpace:
1144 if (!modified) {
1145 if (tPtr->selection.count) {
1146 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1147 data = (void *)WMDeleteTextEvent;
1148 textEvent = WMTextDidChangeNotification;
1149 } else if (tPtr->cursorPosition > 0) {
1150 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1151 tPtr->cursorPosition);
1152 WMRange range;
1153 range.position = tPtr->cursorPosition + i;
1154 range.count = -i;
1155 WMDeleteTextFieldRange(tPtr, range);
1156 data = (void *)WMDeleteTextEvent;
1157 textEvent = WMTextDidChangeNotification;
1160 relay = False;
1162 break;
1164 case WM_EMACSKEY_DEL:
1165 if (!control_pressed)
1166 goto normal_key;
1167 else
1168 modified = False;
1170 #ifdef XK_KP_Delete
1171 case XK_KP_Delete:
1172 #endif
1173 case XK_Delete:
1174 if (!modified) {
1175 if (tPtr->selection.count) {
1176 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1177 data = (void *)WMDeleteTextEvent;
1178 textEvent = WMTextDidChangeNotification;
1179 } else if (tPtr->cursorPosition < tPtr->textLen) {
1180 WMRange range;
1181 range.position = tPtr->cursorPosition;
1182 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1183 tPtr->textLen - tPtr->cursorPosition);
1184 WMDeleteTextFieldRange(tPtr, range);
1185 data = (void *)WMDeleteTextEvent;
1186 textEvent = WMTextDidChangeNotification;
1189 relay = False;
1191 break;
1193 normal_key:
1194 default:
1195 if (!controled) {
1196 if (count > 0 && !iscntrl(buffer[0])) {
1197 if (tPtr->selection.count)
1198 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1199 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1200 data = (void *)WMInsertTextEvent;
1201 textEvent = WMTextDidChangeNotification;
1203 relay = False;
1206 break;
1209 if (relay) {
1210 WMRelayToNextResponder(W_VIEW(tPtr), event);
1211 return;
1214 /* Do not allow text selection in secure text fields */
1215 if (cancelSelection || tPtr->flags.secure) {
1216 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1218 if (tPtr->selection.count) {
1219 tPtr->selection.count = 0;
1220 refresh = 1;
1222 tPtr->selection.position = tPtr->cursorPosition;
1223 } else {
1224 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1226 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1228 refresh = 1;
1232 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1234 if (textEvent) {
1235 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1237 if (tPtr->delegate) {
1238 if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1239 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1241 else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1242 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1244 else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1245 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1248 WMPostNotification(notif);
1249 WMReleaseNotification(notif);
1252 if (refresh)
1253 paintTextField(tPtr);
1255 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1258 static int pointToCursorPosition(TextField * tPtr, int x)
1260 int a, b, pos, prev, tw;
1262 if (tPtr->flags.bordered)
1263 x -= 2;
1265 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1266 tPtr->textLen - tPtr->viewPosition) <= x)
1267 return tPtr->textLen;
1269 a = tPtr->viewPosition;
1270 b = tPtr->textLen;
1272 /* we halve the text until we get into a 10 byte vicinity of x */
1273 while (b - a > 10) {
1274 pos = (a + b) / 2;
1275 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1276 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1277 if (tw > x) {
1278 b = pos;
1279 } else if (tw < x) {
1280 a = pos;
1281 } else {
1282 return pos;
1286 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1287 * inclusive, with the exception of the left border of the 'a' glyph and
1288 * the right border or the 'b-1' glyph
1290 * ( <--- range for x's position ---> )
1291 * a a+1 .......................... b-1 b
1293 pos = prev = a;
1294 while (pos <= b) {
1295 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1296 if (tw > x) {
1297 return prev;
1298 } else if (pos == b) {
1299 break;
1301 prev = pos;
1302 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1305 return b;
1308 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1310 TextField *tPtr = (TextField *) view->self;
1311 char *str;
1313 tPtr->flags.waitingSelection = 0;
1315 if (data != NULL) {
1316 str = (char *)WMDataBytes(data);
1318 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1319 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1320 } else {
1321 int n;
1323 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1325 if (str != NULL) {
1326 str[n] = 0;
1327 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1328 XFree(str);
1329 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1334 static void handleTextFieldActionEvents(XEvent * event, void *data)
1336 TextField *tPtr = (TextField *) data;
1337 static int move = 0;
1338 static Time lastButtonReleasedEvent = 0;
1339 static Time lastButtonReleasedEvent2 = 0;
1340 Display *dpy = event->xany.display;
1342 CHECK_CLASS(data, WC_TextField);
1344 switch (event->type) {
1345 case KeyPress:
1346 if (tPtr->flags.waitingSelection) {
1347 return;
1349 if (tPtr->flags.enabled && tPtr->flags.focused) {
1350 handleTextFieldKeyPress(tPtr, event);
1351 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1352 tPtr->flags.pointerGrabbed = 1;
1354 break;
1356 case MotionNotify:
1358 if (tPtr->flags.pointerGrabbed) {
1359 tPtr->flags.pointerGrabbed = 0;
1360 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1362 if (tPtr->flags.waitingSelection) {
1363 return;
1366 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1368 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1369 if (WMWidthOfString(tPtr->font,
1370 &(tPtr->text[tPtr->viewPosition]),
1371 tPtr->cursorPosition - tPtr->viewPosition)
1372 > tPtr->usableWidth) {
1373 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1374 tPtr->textLen -
1375 tPtr->viewPosition);
1377 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1378 paintCursor(tPtr);
1379 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1380 tPtr->viewPosition);
1383 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1385 /* Do not allow text selection in secure textfields */
1386 if (tPtr->flags.secure) {
1387 tPtr->selection.position = tPtr->cursorPosition;
1390 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1392 paintCursor(tPtr);
1393 paintTextField(tPtr);
1396 break;
1398 case ButtonPress:
1399 if (tPtr->flags.pointerGrabbed) {
1400 tPtr->flags.pointerGrabbed = 0;
1401 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1402 break;
1405 if (tPtr->flags.waitingSelection) {
1406 break;
1409 move = 1;
1410 switch (tPtr->flags.alignment) {
1411 int textWidth;
1412 case WARight:
1413 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1414 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1415 WMSetFocusToWidget(tPtr);
1417 if (tPtr->flags.focused) {
1418 tPtr->selection.position = tPtr->cursorPosition;
1419 tPtr->selection.count = 0;
1421 if (textWidth < tPtr->usableWidth) {
1422 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1423 event->xbutton.x - tPtr->usableWidth
1424 + textWidth);
1425 } else
1426 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1428 paintTextField(tPtr);
1429 break;
1431 case WALeft:
1432 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1433 WMSetFocusToWidget(tPtr);
1435 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1436 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1437 tPtr->selection.position = tPtr->cursorPosition;
1438 tPtr->selection.count = 0;
1439 paintTextField(tPtr);
1441 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1442 char *text;
1443 int n;
1445 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1446 event->xbutton.time, pasteText, NULL)) {
1447 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1449 if (text) {
1450 text[n] = 0;
1451 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1452 XFree(text);
1453 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1454 (void *)WMInsertTextEvent);
1456 } else {
1457 tPtr->flags.waitingSelection = 1;
1460 break;
1461 default:
1462 break;
1464 break;
1466 case ButtonRelease:
1467 if (tPtr->flags.pointerGrabbed) {
1468 tPtr->flags.pointerGrabbed = 0;
1469 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1471 if (tPtr->flags.waitingSelection) {
1472 break;
1475 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1476 int start, count;
1477 XRotateBuffers(dpy, 1);
1479 count = abs(tPtr->selection.count);
1480 if (tPtr->selection.count < 0)
1481 start = tPtr->selection.position - count;
1482 else
1483 start = tPtr->selection.position;
1485 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1488 move = 0;
1490 if (!tPtr->flags.secure &&
1491 event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1493 if (event->xbutton.time - lastButtonReleasedEvent2 <=
1494 2 * WINGsConfiguration.doubleClickDelay) {
1495 tPtr->selection.position = 0;
1496 tPtr->selection.count = tPtr->textLen;
1497 } else {
1498 int pos, cnt;
1499 char *txt;
1500 pos = tPtr->selection.position;
1501 cnt = tPtr->selection.count;
1502 txt = tPtr->text;
1503 while (pos >= 0) {
1504 if (txt[pos] == ' ' || txt[pos] == '\t')
1505 break;
1506 pos--;
1508 pos++;
1510 while (pos + cnt < tPtr->textLen) {
1511 if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1512 break;
1513 cnt++;
1515 tPtr->selection.position = pos;
1516 tPtr->selection.count = cnt;
1518 paintTextField(tPtr);
1520 if (!tPtr->flags.ownsSelection) {
1521 tPtr->flags.ownsSelection =
1522 WMCreateSelectionHandler(tPtr->view,
1523 XA_PRIMARY,
1524 event->xbutton.time, &selectionHandler, NULL);
1526 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1527 tPtr->flags.ownsSelection =
1528 WMCreateSelectionHandler(tPtr->view,
1529 XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1532 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1533 lastButtonReleasedEvent = event->xbutton.time;
1535 break;
1539 static void destroyTextField(TextField * tPtr)
1541 #if 0
1542 if (tPtr->timerID)
1543 WMDeleteTimerHandler(tPtr->timerID);
1544 #endif
1546 W_DestroyIC(tPtr->view);
1548 WMReleaseFont(tPtr->font);
1549 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1550 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1551 WMRemoveNotificationObserver(tPtr);
1553 if (tPtr->text)
1554 wfree(tPtr->text);
1556 wfree(tPtr);