WINGs: Added 'const' attribute to function 'WMCreateHashTable'
[wmaker-crm.git] / WINGs / wtextfield.c
blobde7124494977c1cfe1772f9c8f605e5561ca1d49
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 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 *ptr;
267 ptr = wmalloc(4 * sizeof(Atom));
268 ptr[0] = _TARGETS;
269 ptr[1] = XA_STRING;
270 ptr[2] = TEXT;
271 ptr[3] = COMPOUND_TEXT;
273 data = WMCreateDataWithBytes(ptr, 4 * 4);
274 WMSetDataFormat(data, 32);
276 *type = target;
277 return data;
280 return NULL;
284 static void lostSelection(WMView * view, Atom selection, void *cdata)
286 TextField *tPtr = (WMTextField *) view->self;
288 /* Parameter not used, but tell the compiler that it is ok */
289 (void) cdata;
291 if (tPtr->flags.ownsSelection) {
292 WMDeleteSelectionHandler(view, selection, CurrentTime);
293 tPtr->flags.ownsSelection = 0;
295 if (tPtr->selection.count != 0) {
296 tPtr->selection.count = 0;
297 paintTextField(tPtr);
301 static void selectionNotification(void *observerData, WMNotification * notification)
303 WMView *observerView = (WMView *) observerData;
304 WMView *newOwnerView = (WMView *) WMGetNotificationClientData(notification);
306 if (observerView != newOwnerView) {
308 //if (tPtr->flags.ownsSelection)
309 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
311 lostSelection(observerView, XA_PRIMARY, NULL);
315 static void realizeObserver(void *self, WMNotification * not)
317 /* Parameter not used, but tell the compiler that it is ok */
318 (void) not;
320 W_CreateIC(((TextField *) self)->view);
323 WMTextField *WMCreateTextField(WMWidget * parent)
325 TextField *tPtr;
327 tPtr = wmalloc(sizeof(TextField));
328 tPtr->widgetClass = WC_TextField;
330 tPtr->view = W_CreateView(W_VIEW(parent));
331 if (!tPtr->view) {
332 wfree(tPtr);
333 return NULL;
335 tPtr->view->self = tPtr;
337 tPtr->view->delegate = &_TextFieldViewDelegate;
339 tPtr->view->attribFlags |= CWCursor;
340 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
342 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
344 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
345 tPtr->textLen = 0;
346 tPtr->bufferSize = MIN_TEXT_BUFFER;
348 tPtr->flags.enabled = 1;
350 WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask | FocusChangeMask, handleEvents, tPtr);
352 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
354 tPtr->flags.bordered = DEFAULT_BORDERED;
355 tPtr->flags.beveled = True;
356 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
357 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
359 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
361 WMCreateEventHandler(tPtr->view, EnterWindowMask | LeaveWindowMask
362 | ButtonReleaseMask | ButtonPressMask | KeyPressMask | Button1MotionMask,
363 handleTextFieldActionEvents, tPtr);
365 WMAddNotificationObserver(selectionNotification, tPtr->view,
366 WMSelectionOwnerDidChangeNotification, (void *)XA_PRIMARY);
368 WMAddNotificationObserver(realizeObserver, tPtr, WMViewRealizedNotification, tPtr->view);
370 tPtr->flags.cursorOn = 1;
372 return tPtr;
375 void WMSetTextFieldDelegate(WMTextField * tPtr, WMTextFieldDelegate * delegate)
377 CHECK_CLASS(tPtr, WC_TextField);
379 tPtr->delegate = delegate;
382 WMTextFieldDelegate *WMGetTextFieldDelegate(WMTextField * tPtr)
384 CHECK_CLASS(tPtr, WC_TextField);
386 return tPtr->delegate;
389 void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
391 int len;
393 CHECK_CLASS(tPtr, WC_TextField);
395 if (!text)
396 return;
398 len = strlen(text);
400 /* check if buffer will hold the text */
401 if (len + tPtr->textLen >= tPtr->bufferSize) {
402 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
403 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
406 if (position < 0 || position >= tPtr->textLen) {
407 /* append the text at the end */
408 wstrlcat(tPtr->text, text, tPtr->bufferSize);
409 tPtr->textLen += len;
410 tPtr->cursorPosition += len;
411 incrToFit(tPtr);
412 } else {
413 /* insert text at position */
414 memmv(&(tPtr->text[position + len]), &(tPtr->text[position]), tPtr->textLen - position + 1);
416 memcpy(&(tPtr->text[position]), text, len);
418 tPtr->textLen += len;
419 if (position >= tPtr->cursorPosition) {
420 tPtr->cursorPosition += len;
421 incrToFit2(tPtr);
422 } else {
423 incrToFit(tPtr);
427 paintTextField(tPtr);
430 void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
432 CHECK_CLASS(tPtr, WC_TextField);
434 normalizeRange(tPtr, &range);
436 if (!range.count)
437 return;
439 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position + range.count]),
440 tPtr->textLen - (range.position + range.count) + 1);
442 /* better than nothing ;) */
443 if (tPtr->cursorPosition > range.position)
444 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition], tPtr->viewPosition);
445 tPtr->textLen -= range.count;
446 tPtr->cursorPosition = range.position;
448 decrToFit(tPtr);
450 paintTextField(tPtr);
453 char *WMGetTextFieldText(WMTextField * tPtr)
455 CHECK_CLASS(tPtr, WC_TextField);
457 return wstrdup(tPtr->text);
460 void WMSetTextFieldText(WMTextField * tPtr, const char *text)
462 CHECK_CLASS(tPtr, WC_TextField);
464 if ((text && strcmp(tPtr->text, text) == 0) || (!text && tPtr->textLen == 0))
465 return;
467 if (text == NULL) {
468 tPtr->text[0] = 0;
469 tPtr->textLen = 0;
470 } else {
471 tPtr->textLen = strlen(text);
473 if (tPtr->textLen >= tPtr->bufferSize) {
474 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
475 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
477 wstrlcpy(tPtr->text, text, tPtr->bufferSize);
480 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
481 tPtr->viewPosition = 0;
482 tPtr->selection.count = 0;
484 if (tPtr->view->flags.realized)
485 paintTextField(tPtr);
488 void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
490 CHECK_CLASS(tPtr, WC_TextField);
492 tPtr->flags.alignment = alignment;
494 if (alignment != WALeft) {
495 wwarning("only left alignment is supported in textfields");
496 return;
499 if (tPtr->view->flags.realized) {
500 paintTextField(tPtr);
504 void WMSetTextFieldBordered(WMTextField * tPtr, Bool bordered)
506 CHECK_CLASS(tPtr, WC_TextField);
508 tPtr->flags.bordered = bordered;
510 if (tPtr->view->flags.realized) {
511 paintTextField(tPtr);
515 void WMSetTextFieldBeveled(WMTextField * tPtr, Bool flag)
517 CHECK_CLASS(tPtr, WC_TextField);
519 tPtr->flags.beveled = ((flag == 0) ? 0 : 1);
521 if (tPtr->view->flags.realized) {
522 paintTextField(tPtr);
526 void WMSetTextFieldSecure(WMTextField * tPtr, Bool flag)
528 CHECK_CLASS(tPtr, WC_TextField);
530 tPtr->flags.secure = ((flag == 0) ? 0 : 1);
532 if (tPtr->view->flags.realized) {
533 paintTextField(tPtr);
537 Bool WMGetTextFieldEditable(WMTextField * tPtr)
539 CHECK_CLASS(tPtr, WC_TextField);
541 return tPtr->flags.enabled;
544 void WMSetTextFieldEditable(WMTextField * tPtr, Bool flag)
546 CHECK_CLASS(tPtr, WC_TextField);
548 tPtr->flags.enabled = ((flag == 0) ? 0 : 1);
550 if (tPtr->view->flags.realized) {
551 paintTextField(tPtr);
555 void WMSelectTextFieldRange(WMTextField * tPtr, WMRange range)
557 CHECK_CLASS(tPtr, WC_TextField);
559 if (tPtr->flags.enabled) {
560 normalizeRange(tPtr, &range);
562 tPtr->selection = range;
564 tPtr->cursorPosition = range.position + range.count;
566 if (tPtr->view->flags.realized) {
567 paintTextField(tPtr);
572 void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position)
574 CHECK_CLASS(tPtr, WC_TextField);
576 if (tPtr->flags.enabled) {
577 if (position > tPtr->textLen)
578 position = tPtr->textLen;
580 tPtr->cursorPosition = position;
581 if (tPtr->view->flags.realized) {
582 paintTextField(tPtr);
587 unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr)
589 CHECK_CLASS(tPtr, WC_TextField);
591 return tPtr->cursorPosition;
594 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
596 CHECK_CLASS(tPtr, WC_TextField);
597 if (next == NULL) {
598 if (tPtr->view->nextFocusChain)
599 tPtr->view->nextFocusChain->prevFocusChain = NULL;
600 tPtr->view->nextFocusChain = NULL;
601 return;
604 CHECK_CLASS(next, WC_TextField);
606 if (tPtr->view->nextFocusChain)
607 tPtr->view->nextFocusChain->prevFocusChain = NULL;
608 if (next->view->prevFocusChain)
609 next->view->prevFocusChain->nextFocusChain = NULL;
611 tPtr->view->nextFocusChain = next->view;
612 next->view->prevFocusChain = tPtr->view;
615 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
617 CHECK_CLASS(tPtr, WC_TextField);
618 if (prev == NULL) {
619 if (tPtr->view->prevFocusChain)
620 tPtr->view->prevFocusChain->nextFocusChain = NULL;
621 tPtr->view->prevFocusChain = NULL;
622 return;
625 CHECK_CLASS(prev, WC_TextField);
627 if (tPtr->view->prevFocusChain)
628 tPtr->view->prevFocusChain->nextFocusChain = NULL;
629 if (prev->view->nextFocusChain)
630 prev->view->nextFocusChain->prevFocusChain = NULL;
632 tPtr->view->prevFocusChain = prev->view;
633 prev->view->nextFocusChain = tPtr->view;
636 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
638 CHECK_CLASS(tPtr, WC_TextField);
640 if (tPtr->font)
641 WMReleaseFont(tPtr->font);
642 tPtr->font = WMRetainFont(font);
644 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
646 if (tPtr->view->flags.realized) {
647 paintTextField(tPtr);
651 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
653 return tPtr->font;
656 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
658 WMTextField *tPtr = (WMTextField *) view->self;
660 /* Parameter not used, but tell the compiler that it is ok */
661 (void) self;
663 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
665 tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth /*+ 2 */ ;
668 static char *makeHiddenString(int length)
670 char *data = wmalloc(length + 1);
672 memset(data, '*', length);
673 data[length] = '\0';
675 return data;
678 static void paintCursor(TextField * tPtr)
680 int cx;
681 WMScreen *screen = tPtr->view->screen;
682 int textWidth;
683 char *text;
685 if (tPtr->flags.secure)
686 text = makeHiddenString(strlen(tPtr->text));
687 else
688 text = tPtr->text;
690 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
692 switch (tPtr->flags.alignment) {
693 case WARight:
694 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
695 if (textWidth < tPtr->usableWidth)
696 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
697 else
698 cx += tPtr->offsetWidth + 1;
699 break;
700 case WALeft:
701 cx += tPtr->offsetWidth + 1;
702 break;
703 case WAJustified:
704 /* not supported */
705 case WACenter:
706 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
707 if (textWidth < tPtr->usableWidth)
708 cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
709 else
710 cx += tPtr->offsetWidth;
711 break;
714 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
715 cx, tPtr->offsetWidth, 1,
716 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
717 printf("%d %d\n",cx,tPtr->cursorPosition);
720 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
721 cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
723 W_SetPreeditPositon(tPtr->view, cx, 0);
725 if (tPtr->flags.secure) {
726 wfree(text);
730 static void drawRelief(WMView * view, Bool beveled)
732 WMScreen *scr = view->screen;
733 Display *dpy = scr->display;
734 GC wgc;
735 GC lgc;
736 GC dgc;
737 int width = view->size.width;
738 int height = view->size.height;
740 dgc = WMColorGC(scr->darkGray);
742 if (!beveled) {
743 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
745 return;
747 wgc = WMColorGC(scr->white);
748 lgc = WMColorGC(scr->gray);
750 /* top left */
751 XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
752 XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
754 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
755 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
757 /* bottom right */
758 XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
759 XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
761 XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
762 XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
765 static void paintTextField(TextField * tPtr)
767 W_Screen *screen = tPtr->view->screen;
768 W_View *view = tPtr->view;
769 W_View viewbuffer;
770 int tx, ty, tw;
771 int rx;
772 int bd;
773 int totalWidth;
774 char *text;
775 Pixmap drawbuffer;
776 WMColor *color;
778 if (!view->flags.realized || !view->flags.mapped)
779 return;
781 if (!tPtr->flags.bordered) {
782 bd = 0;
783 } else {
784 bd = 2;
787 if (tPtr->flags.secure) {
788 text = makeHiddenString(strlen(tPtr->text));
789 } else {
790 text = tPtr->text;
793 totalWidth = tPtr->view->size.width - 2 * bd;
795 drawbuffer = XCreatePixmap(screen->display, view->window,
796 view->size.width, view->size.height, screen->depth);
797 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
798 0, 0, view->size.width, view->size.height);
799 /* this is quite dirty */
800 viewbuffer.screen = view->screen;
801 viewbuffer.size = view->size;
802 viewbuffer.window = drawbuffer;
804 if (tPtr->textLen > 0) {
805 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
807 ty = tPtr->offsetWidth;
808 switch (tPtr->flags.alignment) {
809 case WALeft:
810 tx = tPtr->offsetWidth + 1;
811 if (tw < tPtr->usableWidth)
812 XFillRectangle(screen->display, drawbuffer,
813 WMColorGC(screen->white),
814 bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
815 break;
817 case WACenter:
818 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
819 if (tw < tPtr->usableWidth)
820 XClearArea(screen->display, view->window, bd, bd,
821 totalWidth, view->size.height - 2 * bd, False);
822 break;
824 default:
825 case WARight:
826 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
827 if (tw < tPtr->usableWidth)
828 XClearArea(screen->display, view->window, bd, bd,
829 totalWidth - tw, view->size.height - 2 * bd, False);
830 break;
833 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
835 WMDrawImageString(screen, drawbuffer, color, screen->white,
836 tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
837 tPtr->textLen - tPtr->viewPosition);
839 if (tPtr->selection.count) {
840 int count, count2;
842 count = tPtr->selection.count < 0
843 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
844 count2 = abs(tPtr->selection.count);
845 if (count < tPtr->viewPosition) {
846 count2 = abs(count2 - abs(tPtr->viewPosition - count));
847 count = tPtr->viewPosition;
850 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
851 - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
853 WMDrawImageString(screen, drawbuffer, color, screen->gray,
854 tPtr->font, rx, ty, &(text[count]), count2);
856 } else {
857 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
858 bd, bd, totalWidth, view->size.height - 2 * bd);
861 /* draw relief */
862 if (tPtr->flags.bordered) {
863 drawRelief(&viewbuffer, tPtr->flags.beveled);
866 if (tPtr->flags.secure)
867 wfree(text);
868 XCopyArea(screen->display, drawbuffer, view->window,
869 screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
870 XFreePixmap(screen->display, drawbuffer);
872 /* draw cursor */
873 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
874 paintCursor(tPtr);
878 static void blinkCursor(void *data)
880 TextField *tPtr = (TextField *) data;
882 if (tPtr->flags.cursorOn) {
883 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor, data);
884 } else {
885 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, data);
887 paintCursor(tPtr);
888 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
891 static void handleEvents(XEvent * event, void *data)
893 TextField *tPtr = (TextField *) data;
895 CHECK_CLASS(data, WC_TextField);
897 switch (event->type) {
898 case FocusIn:
899 W_FocusIC(tPtr->view);
900 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
901 return;
902 tPtr->flags.focused = 1;
904 if (!tPtr->timerID) {
905 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
908 paintTextField(tPtr);
910 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
912 tPtr->flags.notIllegalMovement = 0;
913 break;
915 case FocusOut:
916 W_UnFocusIC(tPtr->view);
917 tPtr->flags.focused = 0;
919 if (tPtr->timerID)
920 WMDeleteTimerHandler(tPtr->timerID);
921 tPtr->timerID = NULL;
923 paintTextField(tPtr);
924 if (!tPtr->flags.notIllegalMovement) {
925 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
926 (void *)WMIllegalTextMovement);
928 break;
930 case Expose:
931 if (event->xexpose.count != 0)
932 break;
933 paintTextField(tPtr);
934 break;
936 case DestroyNotify:
937 destroyTextField(tPtr);
938 break;
942 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
944 char buffer[64];
945 KeySym ksym;
946 char *textEvent = NULL;
947 void *data = NULL;
948 int count, refresh = 0;
949 int control_pressed = 0;
950 int cancelSelection = 1;
951 Bool shifted, controled, modified;
952 Bool relay = True;
954 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
955 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
956 control_pressed = 1;
958 shifted = (event->xkey.state & ShiftMask ? True : False);
959 controled = (event->xkey.state & ControlMask ? True : False);
960 modified = shifted || controled;
962 count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
963 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
964 buffer[count] = '\0';
966 switch (ksym) {
967 case XK_Tab:
968 #ifdef XK_ISO_Left_Tab
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 case XK_Return:
1005 if (!modified) {
1006 data = (void *)WMReturnTextMovement;
1007 textEvent = WMTextDidEndEditingNotification;
1009 relay = False;
1011 break;
1013 case WM_EMACSKEY_LEFT:
1014 if (!control_pressed)
1015 goto normal_key;
1016 else
1017 controled = False;
1019 #ifdef XK_KP_Left
1020 case XK_KP_Left:
1021 #endif
1022 case XK_Left:
1023 if (tPtr->cursorPosition > 0) {
1024 int i;
1025 paintCursor(tPtr);
1027 i = tPtr->cursorPosition;
1028 i += oneUTF8CharBackward(&tPtr->text[i], i);
1029 if (controled) {
1030 while (i > 0 && tPtr->text[i] != ' ')
1031 i--;
1032 while (i > 0 && tPtr->text[i] == ' ')
1033 i--;
1035 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1036 } else
1037 tPtr->cursorPosition = i;
1039 if (tPtr->cursorPosition < tPtr->viewPosition) {
1040 tPtr->viewPosition = tPtr->cursorPosition;
1041 refresh = 1;
1042 } else
1043 paintCursor(tPtr);
1045 if (shifted)
1046 cancelSelection = 0;
1048 relay = False;
1050 break;
1052 case WM_EMACSKEY_RIGHT:
1053 if (!control_pressed)
1054 goto normal_key;
1055 else
1056 controled = False;
1058 #ifdef XK_KP_Right
1059 case XK_KP_Right:
1060 #endif
1061 case XK_Right:
1062 if (tPtr->cursorPosition < tPtr->textLen) {
1063 int i;
1064 paintCursor(tPtr);
1066 i = tPtr->cursorPosition;
1067 if (controled) {
1068 while (tPtr->text[i] && tPtr->text[i] != ' ')
1069 i++;
1070 while (tPtr->text[i] == ' ')
1071 i++;
1072 } else {
1073 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1075 tPtr->cursorPosition = i;
1077 refresh = incrToFit2(tPtr);
1079 if (!refresh)
1080 paintCursor(tPtr);
1082 if (shifted)
1083 cancelSelection = 0;
1085 relay = False;
1087 break;
1089 case WM_EMACSKEY_HOME:
1090 if (!control_pressed)
1091 goto normal_key;
1092 else
1093 controled = False;
1095 #ifdef XK_KP_Home
1096 case XK_KP_Home:
1097 #endif
1098 case XK_Home:
1099 if (!controled) {
1100 if (tPtr->cursorPosition > 0) {
1101 paintCursor(tPtr);
1102 tPtr->cursorPosition = 0;
1103 if (tPtr->viewPosition > 0) {
1104 tPtr->viewPosition = 0;
1105 refresh = 1;
1106 } else
1107 paintCursor(tPtr);
1109 if (shifted)
1110 cancelSelection = 0;
1112 relay = False;
1114 break;
1116 case WM_EMACSKEY_END:
1117 if (!control_pressed)
1118 goto normal_key;
1119 else
1120 controled = False;
1122 #ifdef XK_KP_End
1123 case XK_KP_End:
1124 #endif
1125 case XK_End:
1126 if (!controled) {
1127 if (tPtr->cursorPosition < tPtr->textLen) {
1128 paintCursor(tPtr);
1129 tPtr->cursorPosition = tPtr->textLen;
1130 tPtr->viewPosition = 0;
1132 refresh = incrToFit(tPtr);
1134 if (!refresh)
1135 paintCursor(tPtr);
1137 if (shifted)
1138 cancelSelection = 0;
1140 relay = False;
1142 break;
1144 case WM_EMACSKEY_BS:
1145 if (!control_pressed)
1146 goto normal_key;
1147 else
1148 modified = False;
1150 case XK_BackSpace:
1151 if (!modified) {
1152 if (tPtr->selection.count) {
1153 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1154 data = (void *)WMDeleteTextEvent;
1155 textEvent = WMTextDidChangeNotification;
1156 } else if (tPtr->cursorPosition > 0) {
1157 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1158 tPtr->cursorPosition);
1159 WMRange range;
1160 range.position = tPtr->cursorPosition + i;
1161 range.count = -i;
1162 WMDeleteTextFieldRange(tPtr, range);
1163 data = (void *)WMDeleteTextEvent;
1164 textEvent = WMTextDidChangeNotification;
1167 relay = False;
1169 break;
1171 case WM_EMACSKEY_DEL:
1172 if (!control_pressed)
1173 goto normal_key;
1174 else
1175 modified = False;
1177 #ifdef XK_KP_Delete
1178 case XK_KP_Delete:
1179 #endif
1180 case XK_Delete:
1181 if (!modified) {
1182 if (tPtr->selection.count) {
1183 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1184 data = (void *)WMDeleteTextEvent;
1185 textEvent = WMTextDidChangeNotification;
1186 } else if (tPtr->cursorPosition < tPtr->textLen) {
1187 WMRange range;
1188 range.position = tPtr->cursorPosition;
1189 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1190 tPtr->textLen - tPtr->cursorPosition);
1191 WMDeleteTextFieldRange(tPtr, range);
1192 data = (void *)WMDeleteTextEvent;
1193 textEvent = WMTextDidChangeNotification;
1196 relay = False;
1198 break;
1200 normal_key:
1201 default:
1202 if (!controled) {
1203 if (count > 0 && !iscntrl(buffer[0])) {
1204 if (tPtr->selection.count)
1205 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1206 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1207 data = (void *)WMInsertTextEvent;
1208 textEvent = WMTextDidChangeNotification;
1210 relay = False;
1213 break;
1216 if (relay) {
1217 WMRelayToNextResponder(W_VIEW(tPtr), event);
1218 return;
1221 /* Do not allow text selection in secure text fields */
1222 if (cancelSelection || tPtr->flags.secure) {
1223 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1225 if (tPtr->selection.count) {
1226 tPtr->selection.count = 0;
1227 refresh = 1;
1229 tPtr->selection.position = tPtr->cursorPosition;
1230 } else {
1231 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1233 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1235 refresh = 1;
1239 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1241 if (textEvent) {
1242 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1244 if (tPtr->delegate) {
1245 if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1246 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1248 else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1249 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1251 else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1252 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1255 WMPostNotification(notif);
1256 WMReleaseNotification(notif);
1259 if (refresh)
1260 paintTextField(tPtr);
1262 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1265 static int pointToCursorPosition(TextField * tPtr, int x)
1267 int a, b, pos, prev, tw;
1269 if (tPtr->flags.bordered)
1270 x -= 2;
1272 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1273 tPtr->textLen - tPtr->viewPosition) <= x)
1274 return tPtr->textLen;
1276 a = tPtr->viewPosition;
1277 b = tPtr->textLen;
1279 /* we halve the text until we get into a 10 byte vicinity of x */
1280 while (b - a > 10) {
1281 pos = (a + b) / 2;
1282 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1283 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1284 if (tw > x) {
1285 b = pos;
1286 } else if (tw < x) {
1287 a = pos;
1288 } else {
1289 return pos;
1293 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1294 * inclusive, with the exception of the left border of the 'a' glyph and
1295 * the right border or the 'b-1' glyph
1297 * ( <--- range for x's position ---> )
1298 * a a+1 .......................... b-1 b
1300 pos = prev = a;
1301 while (pos <= b) {
1302 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1303 if (tw > x) {
1304 return prev;
1305 } else if (pos == b) {
1306 break;
1308 prev = pos;
1309 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1312 return b;
1315 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1317 TextField *tPtr = (TextField *) view->self;
1318 char *str;
1320 /* Parameter not used, but tell the compiler that it is ok */
1321 (void) selection;
1322 (void) target;
1323 (void) timestamp;
1324 (void) cdata;
1326 tPtr->flags.waitingSelection = 0;
1328 if (data != NULL) {
1329 str = (char *)WMDataBytes(data);
1331 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1332 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1333 } else {
1334 int n;
1336 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1338 if (str != NULL) {
1339 str[n] = 0;
1340 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1341 XFree(str);
1342 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1347 static void handleTextFieldActionEvents(XEvent * event, void *data)
1349 TextField *tPtr = (TextField *) data;
1350 static Time lastButtonReleasedEvent = 0;
1351 static Time lastButtonReleasedEvent2 = 0;
1352 Display *dpy = event->xany.display;
1354 CHECK_CLASS(data, WC_TextField);
1356 switch (event->type) {
1357 case KeyPress:
1358 if (tPtr->flags.waitingSelection) {
1359 return;
1361 if (tPtr->flags.enabled && tPtr->flags.focused) {
1362 handleTextFieldKeyPress(tPtr, event);
1363 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1364 tPtr->flags.pointerGrabbed = 1;
1366 break;
1368 case MotionNotify:
1370 if (tPtr->flags.pointerGrabbed) {
1371 tPtr->flags.pointerGrabbed = 0;
1372 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1374 if (tPtr->flags.waitingSelection) {
1375 return;
1378 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1380 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1381 if (WMWidthOfString(tPtr->font,
1382 &(tPtr->text[tPtr->viewPosition]),
1383 tPtr->cursorPosition - tPtr->viewPosition)
1384 > tPtr->usableWidth) {
1385 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1386 tPtr->textLen -
1387 tPtr->viewPosition);
1389 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1390 paintCursor(tPtr);
1391 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1392 tPtr->viewPosition);
1395 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1397 /* Do not allow text selection in secure textfields */
1398 if (tPtr->flags.secure) {
1399 tPtr->selection.position = tPtr->cursorPosition;
1402 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1404 paintCursor(tPtr);
1405 paintTextField(tPtr);
1408 break;
1410 case ButtonPress:
1411 if (tPtr->flags.pointerGrabbed) {
1412 tPtr->flags.pointerGrabbed = 0;
1413 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1414 break;
1417 if (tPtr->flags.waitingSelection) {
1418 break;
1421 switch (tPtr->flags.alignment) {
1422 int textWidth;
1423 case WARight:
1424 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1425 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1426 WMSetFocusToWidget(tPtr);
1428 if (tPtr->flags.focused) {
1429 tPtr->selection.position = tPtr->cursorPosition;
1430 tPtr->selection.count = 0;
1432 if (textWidth < tPtr->usableWidth) {
1433 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1434 event->xbutton.x - tPtr->usableWidth
1435 + textWidth);
1436 } else
1437 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1439 paintTextField(tPtr);
1440 break;
1442 case WALeft:
1443 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1444 WMSetFocusToWidget(tPtr);
1446 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1447 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1448 tPtr->selection.position = tPtr->cursorPosition;
1449 tPtr->selection.count = 0;
1450 paintTextField(tPtr);
1452 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1453 char *text;
1454 int n;
1456 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1457 event->xbutton.time, pasteText, NULL)) {
1458 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1460 if (text) {
1461 text[n] = 0;
1462 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1463 XFree(text);
1464 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1465 (void *)WMInsertTextEvent);
1467 } else {
1468 tPtr->flags.waitingSelection = 1;
1471 break;
1472 default:
1473 break;
1475 break;
1477 case ButtonRelease:
1478 if (tPtr->flags.pointerGrabbed) {
1479 tPtr->flags.pointerGrabbed = 0;
1480 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1482 if (tPtr->flags.waitingSelection) {
1483 break;
1486 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1487 int start, count;
1488 XRotateBuffers(dpy, 1);
1490 count = abs(tPtr->selection.count);
1491 if (tPtr->selection.count < 0)
1492 start = tPtr->selection.position - count;
1493 else
1494 start = tPtr->selection.position;
1496 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1499 if (!tPtr->flags.secure &&
1500 event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1502 if (event->xbutton.time - lastButtonReleasedEvent2 <=
1503 2 * WINGsConfiguration.doubleClickDelay) {
1504 tPtr->selection.position = 0;
1505 tPtr->selection.count = tPtr->textLen;
1506 } else {
1507 int pos, cnt;
1508 char *txt;
1509 pos = tPtr->selection.position;
1510 cnt = tPtr->selection.count;
1511 txt = tPtr->text;
1512 while (pos >= 0) {
1513 if (txt[pos] == ' ' || txt[pos] == '\t')
1514 break;
1515 pos--;
1517 pos++;
1519 while (pos + cnt < tPtr->textLen) {
1520 if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1521 break;
1522 cnt++;
1524 tPtr->selection.position = pos;
1525 tPtr->selection.count = cnt;
1527 paintTextField(tPtr);
1529 if (!tPtr->flags.ownsSelection) {
1530 tPtr->flags.ownsSelection =
1531 WMCreateSelectionHandler(tPtr->view,
1532 XA_PRIMARY,
1533 event->xbutton.time, &selectionHandler, NULL);
1535 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1536 tPtr->flags.ownsSelection =
1537 WMCreateSelectionHandler(tPtr->view,
1538 XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1541 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1542 lastButtonReleasedEvent = event->xbutton.time;
1544 break;
1548 static void destroyTextField(TextField * tPtr)
1550 if (tPtr->timerID)
1551 WMDeleteTimerHandler(tPtr->timerID);
1553 W_DestroyIC(tPtr->view);
1555 WMReleaseFont(tPtr->font);
1556 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1557 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1558 WMRemoveNotificationObserver(tPtr);
1560 if (tPtr->text)
1561 wfree(tPtr->text);
1563 wfree(tPtr);