WPrefs: added option to configure new top and bottom viewports
[wmaker-crm.git] / WINGs / wtextfield.c
blob1b3baa5ac83da825afa085cbe4f4943f091210bd
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 count = tPtr->selection.count < 0
248 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
250 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
252 data = WMCreateDataWithBytes(&(tPtr->text[count]), abs(tPtr->selection.count));
253 WMSetDataFormat(data, 8);
254 *type = target;
256 return data;
259 _TARGETS = XInternAtom(dpy, "TARGETS", False);
260 if (target == _TARGETS) {
261 Atom *ptr;
263 ptr = wmalloc(4 * sizeof(Atom));
264 ptr[0] = _TARGETS;
265 ptr[1] = XA_STRING;
266 ptr[2] = TEXT;
267 ptr[3] = COMPOUND_TEXT;
269 data = WMCreateDataWithBytes(ptr, 4 * 4);
270 WMSetDataFormat(data, 32);
272 *type = target;
273 return data;
276 return NULL;
280 static void lostSelection(WMView * view, Atom selection, void *cdata)
282 TextField *tPtr = (WMTextField *) view->self;
284 if (tPtr->flags.ownsSelection) {
285 WMDeleteSelectionHandler(view, selection, CurrentTime);
286 tPtr->flags.ownsSelection = 0;
288 if (tPtr->selection.count != 0) {
289 tPtr->selection.count = 0;
290 paintTextField(tPtr);
294 static void selectionNotification(void *observerData, WMNotification * notification)
296 WMView *observerView = (WMView *) observerData;
297 WMView *newOwnerView = (WMView *) WMGetNotificationClientData(notification);
299 if (observerView != newOwnerView) {
301 //if (tPtr->flags.ownsSelection)
302 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
304 lostSelection(observerView, XA_PRIMARY, NULL);
308 static void realizeObserver(void *self, WMNotification * not)
310 W_CreateIC(((TextField *) self)->view);
313 WMTextField *WMCreateTextField(WMWidget * parent)
315 TextField *tPtr;
317 tPtr = wmalloc(sizeof(TextField));
318 tPtr->widgetClass = WC_TextField;
320 tPtr->view = W_CreateView(W_VIEW(parent));
321 if (!tPtr->view) {
322 wfree(tPtr);
323 return NULL;
325 tPtr->view->self = tPtr;
327 tPtr->view->delegate = &_TextFieldViewDelegate;
329 tPtr->view->attribFlags |= CWCursor;
330 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
332 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
334 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
335 tPtr->textLen = 0;
336 tPtr->bufferSize = MIN_TEXT_BUFFER;
338 tPtr->flags.enabled = 1;
340 WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask | FocusChangeMask, handleEvents, tPtr);
342 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
344 tPtr->flags.bordered = DEFAULT_BORDERED;
345 tPtr->flags.beveled = True;
346 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
347 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
349 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
351 WMCreateEventHandler(tPtr->view, EnterWindowMask | LeaveWindowMask
352 | ButtonReleaseMask | ButtonPressMask | KeyPressMask | Button1MotionMask,
353 handleTextFieldActionEvents, tPtr);
355 WMAddNotificationObserver(selectionNotification, tPtr->view,
356 WMSelectionOwnerDidChangeNotification, (void *)XA_PRIMARY);
358 WMAddNotificationObserver(realizeObserver, tPtr, WMViewRealizedNotification, tPtr->view);
360 tPtr->flags.cursorOn = 1;
362 return tPtr;
365 void WMSetTextFieldDelegate(WMTextField * tPtr, WMTextFieldDelegate * delegate)
367 CHECK_CLASS(tPtr, WC_TextField);
369 tPtr->delegate = delegate;
372 WMTextFieldDelegate *WMGetTextFieldDelegate(WMTextField * tPtr)
374 CHECK_CLASS(tPtr, WC_TextField);
376 return tPtr->delegate;
379 void WMInsertTextFieldText(WMTextField * tPtr, const char *text, int position)
381 int len;
383 CHECK_CLASS(tPtr, WC_TextField);
385 if (!text)
386 return;
388 len = strlen(text);
390 /* check if buffer will hold the text */
391 if (len + tPtr->textLen >= tPtr->bufferSize) {
392 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
393 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
396 if (position < 0 || position >= tPtr->textLen) {
397 /* append the text at the end */
398 wstrlcat(tPtr->text, text, tPtr->bufferSize);
399 tPtr->textLen += len;
400 tPtr->cursorPosition += len;
401 incrToFit(tPtr);
402 } else {
403 /* insert text at position */
404 memmv(&(tPtr->text[position + len]), &(tPtr->text[position]), tPtr->textLen - position + 1);
406 memcpy(&(tPtr->text[position]), text, len);
408 tPtr->textLen += len;
409 if (position >= tPtr->cursorPosition) {
410 tPtr->cursorPosition += len;
411 incrToFit2(tPtr);
412 } else {
413 incrToFit(tPtr);
417 paintTextField(tPtr);
420 void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
422 CHECK_CLASS(tPtr, WC_TextField);
424 normalizeRange(tPtr, &range);
426 if (!range.count)
427 return;
429 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position + range.count]),
430 tPtr->textLen - (range.position + range.count) + 1);
432 /* better than nothing ;) */
433 if (tPtr->cursorPosition > range.position)
434 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition], tPtr->viewPosition);
435 tPtr->textLen -= range.count;
436 tPtr->cursorPosition = range.position;
438 decrToFit(tPtr);
440 paintTextField(tPtr);
443 char *WMGetTextFieldText(WMTextField * tPtr)
445 CHECK_CLASS(tPtr, WC_TextField);
447 return wstrdup(tPtr->text);
450 void WMSetTextFieldText(WMTextField * tPtr, const char *text)
452 CHECK_CLASS(tPtr, WC_TextField);
454 if ((text && strcmp(tPtr->text, text) == 0) || (!text && tPtr->textLen == 0))
455 return;
457 if (text == NULL) {
458 tPtr->text[0] = 0;
459 tPtr->textLen = 0;
460 } else {
461 tPtr->textLen = strlen(text);
463 if (tPtr->textLen >= tPtr->bufferSize) {
464 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
465 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
467 wstrlcpy(tPtr->text, text, tPtr->bufferSize);
470 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
471 tPtr->viewPosition = 0;
472 tPtr->selection.count = 0;
474 if (tPtr->view->flags.realized)
475 paintTextField(tPtr);
478 void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
480 CHECK_CLASS(tPtr, WC_TextField);
482 tPtr->flags.alignment = alignment;
484 if (alignment != WALeft) {
485 wwarning("only left alignment is supported in textfields");
486 return;
489 if (tPtr->view->flags.realized) {
490 paintTextField(tPtr);
494 void WMSetTextFieldBordered(WMTextField * tPtr, Bool bordered)
496 CHECK_CLASS(tPtr, WC_TextField);
498 tPtr->flags.bordered = bordered;
500 if (tPtr->view->flags.realized) {
501 paintTextField(tPtr);
505 void WMSetTextFieldBeveled(WMTextField * tPtr, Bool flag)
507 CHECK_CLASS(tPtr, WC_TextField);
509 tPtr->flags.beveled = ((flag == 0) ? 0 : 1);
511 if (tPtr->view->flags.realized) {
512 paintTextField(tPtr);
516 void WMSetTextFieldSecure(WMTextField * tPtr, Bool flag)
518 CHECK_CLASS(tPtr, WC_TextField);
520 tPtr->flags.secure = ((flag == 0) ? 0 : 1);
522 if (tPtr->view->flags.realized) {
523 paintTextField(tPtr);
527 Bool WMGetTextFieldEditable(WMTextField * tPtr)
529 CHECK_CLASS(tPtr, WC_TextField);
531 return tPtr->flags.enabled;
534 void WMSetTextFieldEditable(WMTextField * tPtr, Bool flag)
536 CHECK_CLASS(tPtr, WC_TextField);
538 tPtr->flags.enabled = ((flag == 0) ? 0 : 1);
540 if (tPtr->view->flags.realized) {
541 paintTextField(tPtr);
545 void WMSelectTextFieldRange(WMTextField * tPtr, WMRange range)
547 CHECK_CLASS(tPtr, WC_TextField);
549 if (tPtr->flags.enabled) {
550 normalizeRange(tPtr, &range);
552 tPtr->selection = range;
554 tPtr->cursorPosition = range.position + range.count;
556 if (tPtr->view->flags.realized) {
557 paintTextField(tPtr);
562 void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position)
564 CHECK_CLASS(tPtr, WC_TextField);
566 if (tPtr->flags.enabled) {
567 if (position > tPtr->textLen)
568 position = tPtr->textLen;
570 tPtr->cursorPosition = position;
571 if (tPtr->view->flags.realized) {
572 paintTextField(tPtr);
577 unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr)
579 CHECK_CLASS(tPtr, WC_TextField);
581 return tPtr->cursorPosition;
584 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
586 CHECK_CLASS(tPtr, WC_TextField);
587 if (next == NULL) {
588 if (tPtr->view->nextFocusChain)
589 tPtr->view->nextFocusChain->prevFocusChain = NULL;
590 tPtr->view->nextFocusChain = NULL;
591 return;
594 CHECK_CLASS(next, WC_TextField);
596 if (tPtr->view->nextFocusChain)
597 tPtr->view->nextFocusChain->prevFocusChain = NULL;
598 if (next->view->prevFocusChain)
599 next->view->prevFocusChain->nextFocusChain = NULL;
601 tPtr->view->nextFocusChain = next->view;
602 next->view->prevFocusChain = tPtr->view;
605 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
607 CHECK_CLASS(tPtr, WC_TextField);
608 if (prev == NULL) {
609 if (tPtr->view->prevFocusChain)
610 tPtr->view->prevFocusChain->nextFocusChain = NULL;
611 tPtr->view->prevFocusChain = NULL;
612 return;
615 CHECK_CLASS(prev, WC_TextField);
617 if (tPtr->view->prevFocusChain)
618 tPtr->view->prevFocusChain->nextFocusChain = NULL;
619 if (prev->view->nextFocusChain)
620 prev->view->nextFocusChain->prevFocusChain = NULL;
622 tPtr->view->prevFocusChain = prev->view;
623 prev->view->nextFocusChain = tPtr->view;
626 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
628 CHECK_CLASS(tPtr, WC_TextField);
630 if (tPtr->font)
631 WMReleaseFont(tPtr->font);
632 tPtr->font = WMRetainFont(font);
634 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
636 if (tPtr->view->flags.realized) {
637 paintTextField(tPtr);
641 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
643 return tPtr->font;
646 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
648 WMTextField *tPtr = (WMTextField *) view->self;
650 tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
652 tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth /*+ 2 */ ;
655 static char *makeHiddenString(int length)
657 char *data = wmalloc(length + 1);
659 memset(data, '*', length);
660 data[length] = '\0';
662 return data;
665 static void paintCursor(TextField * tPtr)
667 int cx;
668 WMScreen *screen = tPtr->view->screen;
669 int textWidth;
670 char *text;
672 if (tPtr->flags.secure)
673 text = makeHiddenString(strlen(tPtr->text));
674 else
675 text = tPtr->text;
677 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
679 switch (tPtr->flags.alignment) {
680 case WARight:
681 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
682 if (textWidth < tPtr->usableWidth)
683 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
684 else
685 cx += tPtr->offsetWidth + 1;
686 break;
687 case WALeft:
688 cx += tPtr->offsetWidth + 1;
689 break;
690 case WAJustified:
691 /* not supported */
692 case WACenter:
693 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
694 if (textWidth < tPtr->usableWidth)
695 cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
696 else
697 cx += tPtr->offsetWidth;
698 break;
701 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
702 cx, tPtr->offsetWidth, 1,
703 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
704 printf("%d %d\n",cx,tPtr->cursorPosition);
707 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
708 cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
710 W_SetPreeditPositon(tPtr->view, cx, 0);
712 if (tPtr->flags.secure) {
713 wfree(text);
717 static void drawRelief(WMView * view, Bool beveled)
719 WMScreen *scr = view->screen;
720 Display *dpy = scr->display;
721 GC wgc;
722 GC lgc;
723 GC dgc;
724 int width = view->size.width;
725 int height = view->size.height;
727 dgc = WMColorGC(scr->darkGray);
729 if (!beveled) {
730 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
732 return;
734 wgc = WMColorGC(scr->white);
735 lgc = WMColorGC(scr->gray);
737 /* top left */
738 XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
739 XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
741 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
742 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
744 /* bottom right */
745 XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
746 XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
748 XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
749 XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
752 static void paintTextField(TextField * tPtr)
754 W_Screen *screen = tPtr->view->screen;
755 W_View *view = tPtr->view;
756 W_View viewbuffer;
757 int tx, ty, tw;
758 int rx;
759 int bd;
760 int totalWidth;
761 char *text;
762 Pixmap drawbuffer;
763 WMColor *color;
765 if (!view->flags.realized || !view->flags.mapped)
766 return;
768 if (!tPtr->flags.bordered) {
769 bd = 0;
770 } else {
771 bd = 2;
774 if (tPtr->flags.secure) {
775 text = makeHiddenString(strlen(tPtr->text));
776 } else {
777 text = tPtr->text;
780 totalWidth = tPtr->view->size.width - 2 * bd;
782 drawbuffer = XCreatePixmap(screen->display, view->window,
783 view->size.width, view->size.height, screen->depth);
784 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
785 0, 0, view->size.width, view->size.height);
786 /* this is quite dirty */
787 viewbuffer.screen = view->screen;
788 viewbuffer.size = view->size;
789 viewbuffer.window = drawbuffer;
791 if (tPtr->textLen > 0) {
792 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
794 ty = tPtr->offsetWidth;
795 switch (tPtr->flags.alignment) {
796 case WALeft:
797 tx = tPtr->offsetWidth + 1;
798 if (tw < tPtr->usableWidth)
799 XFillRectangle(screen->display, drawbuffer,
800 WMColorGC(screen->white),
801 bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
802 break;
804 case WACenter:
805 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
806 if (tw < tPtr->usableWidth)
807 XClearArea(screen->display, view->window, bd, bd,
808 totalWidth, view->size.height - 2 * bd, False);
809 break;
811 default:
812 case WARight:
813 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
814 if (tw < tPtr->usableWidth)
815 XClearArea(screen->display, view->window, bd, bd,
816 totalWidth - tw, view->size.height - 2 * bd, False);
817 break;
820 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
822 WMDrawImageString(screen, drawbuffer, color, screen->white,
823 tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
824 tPtr->textLen - tPtr->viewPosition);
826 if (tPtr->selection.count) {
827 int count, count2;
829 count = tPtr->selection.count < 0
830 ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
831 count2 = abs(tPtr->selection.count);
832 if (count < tPtr->viewPosition) {
833 count2 = abs(count2 - abs(tPtr->viewPosition - count));
834 count = tPtr->viewPosition;
837 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
838 - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
840 WMDrawImageString(screen, drawbuffer, color, screen->gray,
841 tPtr->font, rx, ty, &(text[count]), count2);
843 } else {
844 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
845 bd, bd, totalWidth, view->size.height - 2 * bd);
848 /* draw relief */
849 if (tPtr->flags.bordered) {
850 drawRelief(&viewbuffer, tPtr->flags.beveled);
853 if (tPtr->flags.secure)
854 wfree(text);
855 XCopyArea(screen->display, drawbuffer, view->window,
856 screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
857 XFreePixmap(screen->display, drawbuffer);
859 /* draw cursor */
860 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
861 paintCursor(tPtr);
865 static void blinkCursor(void *data)
867 TextField *tPtr = (TextField *) data;
869 if (tPtr->flags.cursorOn) {
870 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor, data);
871 } else {
872 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, data);
874 paintCursor(tPtr);
875 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
878 static void handleEvents(XEvent * event, void *data)
880 TextField *tPtr = (TextField *) data;
882 CHECK_CLASS(data, WC_TextField);
884 switch (event->type) {
885 case FocusIn:
886 W_FocusIC(tPtr->view);
887 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
888 return;
889 tPtr->flags.focused = 1;
891 if (!tPtr->timerID) {
892 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
895 paintTextField(tPtr);
897 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
899 tPtr->flags.notIllegalMovement = 0;
900 break;
902 case FocusOut:
903 W_UnFocusIC(tPtr->view);
904 tPtr->flags.focused = 0;
906 if (tPtr->timerID)
907 WMDeleteTimerHandler(tPtr->timerID);
908 tPtr->timerID = NULL;
910 paintTextField(tPtr);
911 if (!tPtr->flags.notIllegalMovement) {
912 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
913 (void *)WMIllegalTextMovement);
915 break;
917 case Expose:
918 if (event->xexpose.count != 0)
919 break;
920 paintTextField(tPtr);
921 break;
923 case DestroyNotify:
924 destroyTextField(tPtr);
925 break;
929 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
931 char buffer[64];
932 KeySym ksym;
933 char *textEvent = NULL;
934 void *data = NULL;
935 int count, refresh = 0;
936 int control_pressed = 0;
937 int cancelSelection = 1;
938 Bool shifted, controled, modified;
939 Bool relay = True;
941 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
942 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
943 control_pressed = 1;
945 shifted = (event->xkey.state & ShiftMask ? True : False);
946 controled = (event->xkey.state & ControlMask ? True : False);
947 modified = shifted || controled;
949 count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
950 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
951 buffer[count] = '\0';
953 switch (ksym) {
954 case XK_Tab:
955 #ifdef XK_ISO_Left_Tab
956 case XK_ISO_Left_Tab:
957 #endif
958 if (!controled) {
959 if (shifted) {
960 if (tPtr->view->prevFocusChain) {
961 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
962 tPtr->view->prevFocusChain);
963 tPtr->flags.notIllegalMovement = 1;
965 data = (void *)WMBacktabTextMovement;
966 } else {
967 if (tPtr->view->nextFocusChain) {
968 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
969 tPtr->view->nextFocusChain);
970 tPtr->flags.notIllegalMovement = 1;
972 data = (void *)WMTabTextMovement;
974 textEvent = WMTextDidEndEditingNotification;
976 cancelSelection = 0;
978 relay = False;
980 break;
982 case XK_Escape:
983 if (!modified) {
984 data = (void *)WMEscapeTextMovement;
985 textEvent = WMTextDidEndEditingNotification;
987 relay = False;
989 break;
991 case XK_Return:
992 if (!modified) {
993 data = (void *)WMReturnTextMovement;
994 textEvent = WMTextDidEndEditingNotification;
996 relay = False;
998 break;
1000 case WM_EMACSKEY_LEFT:
1001 if (!control_pressed)
1002 goto normal_key;
1003 else
1004 controled = False;
1006 #ifdef XK_KP_Left
1007 case XK_KP_Left:
1008 #endif
1009 case XK_Left:
1010 if (tPtr->cursorPosition > 0) {
1011 int i;
1012 paintCursor(tPtr);
1014 i = tPtr->cursorPosition;
1015 i += oneUTF8CharBackward(&tPtr->text[i], i);
1016 if (controled) {
1017 while (i > 0 && tPtr->text[i] != ' ')
1018 i--;
1019 while (i > 0 && tPtr->text[i] == ' ')
1020 i--;
1022 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1023 } else
1024 tPtr->cursorPosition = i;
1026 if (tPtr->cursorPosition < tPtr->viewPosition) {
1027 tPtr->viewPosition = tPtr->cursorPosition;
1028 refresh = 1;
1029 } else
1030 paintCursor(tPtr);
1032 if (shifted)
1033 cancelSelection = 0;
1035 relay = False;
1037 break;
1039 case WM_EMACSKEY_RIGHT:
1040 if (!control_pressed)
1041 goto normal_key;
1042 else
1043 controled = False;
1045 #ifdef XK_KP_Right
1046 case XK_KP_Right:
1047 #endif
1048 case XK_Right:
1049 if (tPtr->cursorPosition < tPtr->textLen) {
1050 int i;
1051 paintCursor(tPtr);
1053 i = tPtr->cursorPosition;
1054 if (controled) {
1055 while (tPtr->text[i] && tPtr->text[i] != ' ')
1056 i++;
1057 while (tPtr->text[i] == ' ')
1058 i++;
1059 } else {
1060 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1062 tPtr->cursorPosition = i;
1064 refresh = incrToFit2(tPtr);
1066 if (!refresh)
1067 paintCursor(tPtr);
1069 if (shifted)
1070 cancelSelection = 0;
1072 relay = False;
1074 break;
1076 case WM_EMACSKEY_HOME:
1077 if (!control_pressed)
1078 goto normal_key;
1079 else
1080 controled = False;
1082 #ifdef XK_KP_Home
1083 case XK_KP_Home:
1084 #endif
1085 case XK_Home:
1086 if (!controled) {
1087 if (tPtr->cursorPosition > 0) {
1088 paintCursor(tPtr);
1089 tPtr->cursorPosition = 0;
1090 if (tPtr->viewPosition > 0) {
1091 tPtr->viewPosition = 0;
1092 refresh = 1;
1093 } else
1094 paintCursor(tPtr);
1096 if (shifted)
1097 cancelSelection = 0;
1099 relay = False;
1101 break;
1103 case WM_EMACSKEY_END:
1104 if (!control_pressed)
1105 goto normal_key;
1106 else
1107 controled = False;
1109 #ifdef XK_KP_End
1110 case XK_KP_End:
1111 #endif
1112 case XK_End:
1113 if (!controled) {
1114 if (tPtr->cursorPosition < tPtr->textLen) {
1115 paintCursor(tPtr);
1116 tPtr->cursorPosition = tPtr->textLen;
1117 tPtr->viewPosition = 0;
1119 refresh = incrToFit(tPtr);
1121 if (!refresh)
1122 paintCursor(tPtr);
1124 if (shifted)
1125 cancelSelection = 0;
1127 relay = False;
1129 break;
1131 case WM_EMACSKEY_BS:
1132 if (!control_pressed)
1133 goto normal_key;
1134 else
1135 modified = False;
1137 case XK_BackSpace:
1138 if (!modified) {
1139 if (tPtr->selection.count) {
1140 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1141 data = (void *)WMDeleteTextEvent;
1142 textEvent = WMTextDidChangeNotification;
1143 } else if (tPtr->cursorPosition > 0) {
1144 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1145 tPtr->cursorPosition);
1146 WMRange range;
1147 range.position = tPtr->cursorPosition + i;
1148 range.count = -i;
1149 WMDeleteTextFieldRange(tPtr, range);
1150 data = (void *)WMDeleteTextEvent;
1151 textEvent = WMTextDidChangeNotification;
1154 relay = False;
1156 break;
1158 case WM_EMACSKEY_DEL:
1159 if (!control_pressed)
1160 goto normal_key;
1161 else
1162 modified = False;
1164 #ifdef XK_KP_Delete
1165 case XK_KP_Delete:
1166 #endif
1167 case XK_Delete:
1168 if (!modified) {
1169 if (tPtr->selection.count) {
1170 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1171 data = (void *)WMDeleteTextEvent;
1172 textEvent = WMTextDidChangeNotification;
1173 } else if (tPtr->cursorPosition < tPtr->textLen) {
1174 WMRange range;
1175 range.position = tPtr->cursorPosition;
1176 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1177 tPtr->textLen - tPtr->cursorPosition);
1178 WMDeleteTextFieldRange(tPtr, range);
1179 data = (void *)WMDeleteTextEvent;
1180 textEvent = WMTextDidChangeNotification;
1183 relay = False;
1185 break;
1187 normal_key:
1188 default:
1189 if (!controled) {
1190 if (count > 0 && !iscntrl(buffer[0])) {
1191 if (tPtr->selection.count)
1192 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1193 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1194 data = (void *)WMInsertTextEvent;
1195 textEvent = WMTextDidChangeNotification;
1197 relay = False;
1200 break;
1203 if (relay) {
1204 WMRelayToNextResponder(W_VIEW(tPtr), event);
1205 return;
1208 /* Do not allow text selection in secure text fields */
1209 if (cancelSelection || tPtr->flags.secure) {
1210 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1212 if (tPtr->selection.count) {
1213 tPtr->selection.count = 0;
1214 refresh = 1;
1216 tPtr->selection.position = tPtr->cursorPosition;
1217 } else {
1218 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1220 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1222 refresh = 1;
1226 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1228 if (textEvent) {
1229 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1231 if (tPtr->delegate) {
1232 if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1233 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1235 else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1236 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1238 else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1239 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1242 WMPostNotification(notif);
1243 WMReleaseNotification(notif);
1246 if (refresh)
1247 paintTextField(tPtr);
1249 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1252 static int pointToCursorPosition(TextField * tPtr, int x)
1254 int a, b, pos, prev, tw;
1256 if (tPtr->flags.bordered)
1257 x -= 2;
1259 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1260 tPtr->textLen - tPtr->viewPosition) <= x)
1261 return tPtr->textLen;
1263 a = tPtr->viewPosition;
1264 b = tPtr->textLen;
1266 /* we halve the text until we get into a 10 byte vicinity of x */
1267 while (b - a > 10) {
1268 pos = (a + b) / 2;
1269 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1270 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1271 if (tw > x) {
1272 b = pos;
1273 } else if (tw < x) {
1274 a = pos;
1275 } else {
1276 return pos;
1280 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1281 * inclusive, with the exception of the left border of the 'a' glyph and
1282 * the right border or the 'b-1' glyph
1284 * ( <--- range for x's position ---> )
1285 * a a+1 .......................... b-1 b
1287 pos = prev = a;
1288 while (pos <= b) {
1289 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1290 if (tw > x) {
1291 return prev;
1292 } else if (pos == b) {
1293 break;
1295 prev = pos;
1296 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1299 return b;
1302 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1304 TextField *tPtr = (TextField *) view->self;
1305 char *str;
1307 tPtr->flags.waitingSelection = 0;
1309 if (data != NULL) {
1310 str = (char *)WMDataBytes(data);
1312 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1313 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1314 } else {
1315 int n;
1317 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1319 if (str != NULL) {
1320 str[n] = 0;
1321 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1322 XFree(str);
1323 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1328 static void handleTextFieldActionEvents(XEvent * event, void *data)
1330 TextField *tPtr = (TextField *) data;
1331 static Time lastButtonReleasedEvent = 0;
1332 static Time lastButtonReleasedEvent2 = 0;
1333 Display *dpy = event->xany.display;
1335 CHECK_CLASS(data, WC_TextField);
1337 switch (event->type) {
1338 case KeyPress:
1339 if (tPtr->flags.waitingSelection) {
1340 return;
1342 if (tPtr->flags.enabled && tPtr->flags.focused) {
1343 handleTextFieldKeyPress(tPtr, event);
1344 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1345 tPtr->flags.pointerGrabbed = 1;
1347 break;
1349 case MotionNotify:
1351 if (tPtr->flags.pointerGrabbed) {
1352 tPtr->flags.pointerGrabbed = 0;
1353 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1355 if (tPtr->flags.waitingSelection) {
1356 return;
1359 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1361 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1362 if (WMWidthOfString(tPtr->font,
1363 &(tPtr->text[tPtr->viewPosition]),
1364 tPtr->cursorPosition - tPtr->viewPosition)
1365 > tPtr->usableWidth) {
1366 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1367 tPtr->textLen -
1368 tPtr->viewPosition);
1370 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1371 paintCursor(tPtr);
1372 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1373 tPtr->viewPosition);
1376 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1378 /* Do not allow text selection in secure textfields */
1379 if (tPtr->flags.secure) {
1380 tPtr->selection.position = tPtr->cursorPosition;
1383 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1385 paintCursor(tPtr);
1386 paintTextField(tPtr);
1389 break;
1391 case ButtonPress:
1392 if (tPtr->flags.pointerGrabbed) {
1393 tPtr->flags.pointerGrabbed = 0;
1394 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1395 break;
1398 if (tPtr->flags.waitingSelection) {
1399 break;
1402 switch (tPtr->flags.alignment) {
1403 int textWidth;
1404 case WARight:
1405 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1406 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1407 WMSetFocusToWidget(tPtr);
1409 if (tPtr->flags.focused) {
1410 tPtr->selection.position = tPtr->cursorPosition;
1411 tPtr->selection.count = 0;
1413 if (textWidth < tPtr->usableWidth) {
1414 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1415 event->xbutton.x - tPtr->usableWidth
1416 + textWidth);
1417 } else
1418 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1420 paintTextField(tPtr);
1421 break;
1423 case WALeft:
1424 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1425 WMSetFocusToWidget(tPtr);
1427 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1428 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1429 tPtr->selection.position = tPtr->cursorPosition;
1430 tPtr->selection.count = 0;
1431 paintTextField(tPtr);
1433 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1434 char *text;
1435 int n;
1437 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1438 event->xbutton.time, pasteText, NULL)) {
1439 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1441 if (text) {
1442 text[n] = 0;
1443 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1444 XFree(text);
1445 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1446 (void *)WMInsertTextEvent);
1448 } else {
1449 tPtr->flags.waitingSelection = 1;
1452 break;
1453 default:
1454 break;
1456 break;
1458 case ButtonRelease:
1459 if (tPtr->flags.pointerGrabbed) {
1460 tPtr->flags.pointerGrabbed = 0;
1461 XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1463 if (tPtr->flags.waitingSelection) {
1464 break;
1467 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1468 int start, count;
1469 XRotateBuffers(dpy, 1);
1471 count = abs(tPtr->selection.count);
1472 if (tPtr->selection.count < 0)
1473 start = tPtr->selection.position - count;
1474 else
1475 start = tPtr->selection.position;
1477 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1480 if (!tPtr->flags.secure &&
1481 event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1483 if (event->xbutton.time - lastButtonReleasedEvent2 <=
1484 2 * WINGsConfiguration.doubleClickDelay) {
1485 tPtr->selection.position = 0;
1486 tPtr->selection.count = tPtr->textLen;
1487 } else {
1488 int pos, cnt;
1489 char *txt;
1490 pos = tPtr->selection.position;
1491 cnt = tPtr->selection.count;
1492 txt = tPtr->text;
1493 while (pos >= 0) {
1494 if (txt[pos] == ' ' || txt[pos] == '\t')
1495 break;
1496 pos--;
1498 pos++;
1500 while (pos + cnt < tPtr->textLen) {
1501 if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1502 break;
1503 cnt++;
1505 tPtr->selection.position = pos;
1506 tPtr->selection.count = cnt;
1508 paintTextField(tPtr);
1510 if (!tPtr->flags.ownsSelection) {
1511 tPtr->flags.ownsSelection =
1512 WMCreateSelectionHandler(tPtr->view,
1513 XA_PRIMARY,
1514 event->xbutton.time, &selectionHandler, NULL);
1516 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1517 tPtr->flags.ownsSelection =
1518 WMCreateSelectionHandler(tPtr->view,
1519 XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1522 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1523 lastButtonReleasedEvent = event->xbutton.time;
1525 break;
1529 static void destroyTextField(TextField * tPtr)
1531 if (tPtr->timerID)
1532 WMDeleteTimerHandler(tPtr->timerID);
1534 W_DestroyIC(tPtr->view);
1536 WMReleaseFont(tPtr->font);
1537 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1538 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1539 WMRemoveNotificationObserver(tPtr);
1541 if (tPtr->text)
1542 wfree(tPtr->text);
1544 wfree(tPtr);