Add history to some dialog boxes
[wmaker-crm.git] / WINGs / wtextfield.c
1
2 #include "WINGsP.h"
3 #include "wconfig.h"
4
5 #include <X11/keysym.h>
6 #include <X11/Xatom.h>
7
8 #include <ctype.h>
9
10 #define CURSOR_BLINK_ON_DELAY   600
11 #define CURSOR_BLINK_OFF_DELAY  300
12
13 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
14 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
15 char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
16
17 typedef struct W_TextField {
18         W_Class widgetClass;
19         W_View *view;
20
21 #if 0
22         struct W_TextField *nextField;  /* next textfield in the chain */
23         struct W_TextField *prevField;
24 #endif
25
26         char *text;
27         int textLen;            /* size of text */
28         int bufferSize;         /* memory allocated for text */
29
30         int viewPosition;       /* position of text being shown */
31
32         int cursorPosition;     /* position of the insertion cursor */
33
34         short usableWidth;
35         short offsetWidth;      /* offset of text from border */
36
37         WMRange selection;
38
39         WMFont *font;
40
41         WMTextFieldDelegate *delegate;
42
43 #if 0
44         WMHandlerID timerID;    /* for cursor blinking */
45 #endif
46         struct {
47                 WMAlignment alignment:2;
48
49                 unsigned int bordered:1;
50
51                 unsigned int beveled:1;
52
53                 unsigned int enabled:1;
54
55                 unsigned int focused:1;
56
57                 unsigned int cursorOn:1;
58
59                 unsigned int secure:1;  /* password entry style */
60
61                 unsigned int pointerGrabbed:1;
62
63                 unsigned int ownsSelection:1;
64
65                 unsigned int waitingSelection:1;        /* requested selection, but
66                                                          * didnt get yet */
67
68                 unsigned int notIllegalMovement:1;
69         } flags;
70 } TextField;
71
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);}
77
78 #define MIN_TEXT_BUFFER         2
79 #define TEXT_BUFFER_INCR        8
80
81 #define WM_EMACSKEYMASK   ControlMask
82
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
89
90 #define DEFAULT_WIDTH           60
91 #define DEFAULT_HEIGHT          20
92 #define DEFAULT_BORDERED        True
93 #define DEFAULT_ALIGNMENT       WALeft
94
95 static void destroyTextField(TextField * tPtr);
96 static void paintTextField(TextField * tPtr);
97
98 static void handleEvents(XEvent * event, void *data);
99 static void handleTextFieldActionEvents(XEvent * event, void *data);
100 static void didResizeTextField();
101
102 struct W_ViewDelegate _TextFieldViewDelegate = {
103         NULL,
104         NULL,
105         didResizeTextField,
106         NULL,
107         NULL
108 };
109
110 static void lostSelection(WMView * view, Atom selection, void *cdata);
111
112 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type);
113
114 static WMSelectionProcs selectionHandler = {
115         requestHandler,
116         lostSelection,
117         NULL
118 };
119
120 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
121     &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
122
123 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
124     &((tPtr)->text[(start)]), (end) - (start)))
125
126 static INLINE int oneUTF8CharBackward(char *str, int len)
127 {
128         unsigned char *ustr = (unsigned char *)str;
129         int pos = 0;
130
131         while (len-- > 0 && ustr[--pos] >= 0x80 && ustr[pos] <= 0xbf) ;
132         return pos;
133 }
134
135 static INLINE int oneUTF8CharForward(char *str, int len)
136 {
137         unsigned char *ustr = (unsigned char *)str;
138         int pos = 0;
139
140         while (len-- > 0 && ustr[++pos] >= 0x80 && ustr[pos] <= 0xbf) ;
141         return pos;
142 }
143
144 // find the beginning of the UTF8 char pointed by str
145 static INLINE int seekUTF8CharStart(char *str, int len)
146 {
147         unsigned char *ustr = (unsigned char *)str;
148         int pos = 0;
149
150         while (len-- > 0 && ustr[pos] >= 0x80 && ustr[pos] <= 0xbf)
151                 --pos;
152         return pos;
153 }
154
155 static void normalizeRange(TextField * tPtr, WMRange * range)
156 {
157         if (range->position < 0 && range->count < 0)
158                 range->count = 0;
159
160         if (range->count == 0) {
161                 /*range->position = 0; why is this? */
162                 return;
163         }
164
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;
173                 }
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;
181                 }
182         }
183
184         if (range->position + range->count > tPtr->textLen)
185                 range->count = tPtr->textLen - range->position;
186 }
187
188 static void memmv(char *dest, char *src, int size)
189 {
190         int i;
191
192         if (dest > src) {
193                 for (i = size - 1; i >= 0; i--) {
194                         dest[i] = src[i];
195                 }
196         } else if (dest < src) {
197                 for (i = 0; i < size; i++) {
198                         dest[i] = src[i];
199                 }
200         }
201 }
202
203 static int incrToFit(TextField * tPtr)
204 {
205         int vp = tPtr->viewPosition;
206
207         while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
208                 tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
209                                                          tPtr->textLen - tPtr->viewPosition);
210         }
211         return vp != tPtr->viewPosition;
212 }
213
214 static int incrToFit2(TextField * tPtr)
215 {
216         int vp = tPtr->viewPosition;
217
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;
223 }
224
225 static void decrToFit(TextField * tPtr)
226 {
227         int vp = tPtr->viewPosition;
228
229         while (vp > 0 && (vp += oneUTF8CharBackward(&tPtr->text[vp], vp),
230                           TEXT_WIDTH(tPtr, vp)) < tPtr->usableWidth) {
231                 tPtr->viewPosition = vp;
232         }
233 }
234
235 #undef TEXT_WIDTH
236 #undef TEXT_WIDTH2
237
238 static WMData *requestHandler(WMView * view, Atom selection, Atom target, void *cdata, Atom * type)
239 {
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;
247
248         count = tPtr->selection.count < 0
249             ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
250
251         if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
252
253                 data = WMCreateDataWithBytes(&(tPtr->text[count]), abs(tPtr->selection.count));
254                 WMSetDataFormat(data, 8);
255                 *type = target;
256
257                 return data;
258         }
259
260         _TARGETS = XInternAtom(dpy, "TARGETS", False);
261         if (target == _TARGETS) {
262                 Atom *ptr;
263
264                 ptr = wmalloc(4 * sizeof(Atom));
265                 ptr[0] = _TARGETS;
266                 ptr[1] = XA_STRING;
267                 ptr[2] = TEXT;
268                 ptr[3] = COMPOUND_TEXT;
269
270                 data = WMCreateDataWithBytes(ptr, 4 * 4);
271                 WMSetDataFormat(data, 32);
272
273                 *type = target;
274                 return data;
275         }
276
277         return NULL;
278
279 }
280
281 static void lostSelection(WMView * view, Atom selection, void *cdata)
282 {
283         TextField *tPtr = (WMTextField *) view->self;
284
285         if (tPtr->flags.ownsSelection) {
286                 WMDeleteSelectionHandler(view, selection, CurrentTime);
287                 tPtr->flags.ownsSelection = 0;
288         }
289         if (tPtr->selection.count != 0) {
290                 tPtr->selection.count = 0;
291                 paintTextField(tPtr);
292         }
293 }
294
295 static void selectionNotification(void *observerData, WMNotification * notification)
296 {
297         WMView *observerView = (WMView *) observerData;
298         WMView *newOwnerView = (WMView *) WMGetNotificationClientData(notification);
299
300         if (observerView != newOwnerView) {
301                 /*
302                    //if (tPtr->flags.ownsSelection)
303                    //    WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
304                  */
305                 lostSelection(observerView, XA_PRIMARY, NULL);
306         }
307 }
308
309 static void realizeObserver(void *self, WMNotification * not)
310 {
311         W_CreateIC(((TextField *) self)->view);
312 }
313
314 WMTextField *WMCreateTextField(WMWidget * parent)
315 {
316         TextField *tPtr;
317
318         tPtr = wmalloc(sizeof(TextField));
319         memset(tPtr, 0, sizeof(TextField));
320
321         tPtr->widgetClass = WC_TextField;
322
323         tPtr->view = W_CreateView(W_VIEW(parent));
324         if (!tPtr->view) {
325                 wfree(tPtr);
326                 return NULL;
327         }
328         tPtr->view->self = tPtr;
329
330         tPtr->view->delegate = &_TextFieldViewDelegate;
331
332         tPtr->view->attribFlags |= CWCursor;
333         tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
334
335         W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
336
337         tPtr->text = wmalloc(MIN_TEXT_BUFFER);
338         tPtr->text[0] = 0;
339         tPtr->textLen = 0;
340         tPtr->bufferSize = MIN_TEXT_BUFFER;
341
342         tPtr->flags.enabled = 1;
343
344         WMCreateEventHandler(tPtr->view, ExposureMask | StructureNotifyMask | FocusChangeMask, handleEvents, tPtr);
345
346         tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
347
348         tPtr->flags.bordered = DEFAULT_BORDERED;
349         tPtr->flags.beveled = True;
350         tPtr->flags.alignment = DEFAULT_ALIGNMENT;
351         tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
352
353         W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
354
355         WMCreateEventHandler(tPtr->view, EnterWindowMask | LeaveWindowMask
356                              | ButtonReleaseMask | ButtonPressMask | KeyPressMask | Button1MotionMask,
357                              handleTextFieldActionEvents, tPtr);
358
359         WMAddNotificationObserver(selectionNotification, tPtr->view,
360                                   WMSelectionOwnerDidChangeNotification, (void *)XA_PRIMARY);
361
362         WMAddNotificationObserver(realizeObserver, tPtr, WMViewRealizedNotification, tPtr->view);
363
364         tPtr->flags.cursorOn = 1;
365
366         return tPtr;
367 }
368
369 void WMSetTextFieldDelegate(WMTextField * tPtr, WMTextFieldDelegate * delegate)
370 {
371         CHECK_CLASS(tPtr, WC_TextField);
372
373         tPtr->delegate = delegate;
374 }
375
376 WMTextFieldDelegate *WMGetTextFieldDelegate(WMTextField * tPtr)
377 {
378         CHECK_CLASS(tPtr, WC_TextField);
379
380         return tPtr->delegate;
381 }
382
383 void WMInsertTextFieldText(WMTextField * tPtr, char *text, int position)
384 {
385         int len;
386
387         CHECK_CLASS(tPtr, WC_TextField);
388
389         if (!text)
390                 return;
391
392         len = strlen(text);
393
394         /* check if buffer will hold the text */
395         if (len + tPtr->textLen >= tPtr->bufferSize) {
396                 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
397                 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
398         }
399
400         if (position < 0 || position >= tPtr->textLen) {
401                 /* append the text at the end */
402                 strcat(tPtr->text, text);
403                 tPtr->textLen += len;
404                 tPtr->cursorPosition += len;
405                 incrToFit(tPtr);
406         } else {
407                 /* insert text at position */
408                 memmv(&(tPtr->text[position + len]), &(tPtr->text[position]), tPtr->textLen - position + 1);
409
410                 memcpy(&(tPtr->text[position]), text, len);
411
412                 tPtr->textLen += len;
413                 if (position >= tPtr->cursorPosition) {
414                         tPtr->cursorPosition += len;
415                         incrToFit2(tPtr);
416                 } else {
417                         incrToFit(tPtr);
418                 }
419         }
420
421         paintTextField(tPtr);
422 }
423
424 void WMDeleteTextFieldRange(WMTextField * tPtr, WMRange range)
425 {
426         CHECK_CLASS(tPtr, WC_TextField);
427
428         normalizeRange(tPtr, &range);
429
430         if (!range.count)
431                 return;
432
433         memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position + range.count]),
434               tPtr->textLen - (range.position + range.count) + 1);
435
436         /* better than nothing ;) */
437         if (tPtr->cursorPosition > range.position)
438                 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition], tPtr->viewPosition);
439         tPtr->textLen -= range.count;
440         tPtr->cursorPosition = range.position;
441
442         decrToFit(tPtr);
443
444         paintTextField(tPtr);
445 }
446
447 char *WMGetTextFieldText(WMTextField * tPtr)
448 {
449         CHECK_CLASS(tPtr, WC_TextField);
450
451         return wstrdup(tPtr->text);
452 }
453
454 void WMSetTextFieldText(WMTextField * tPtr, char *text)
455 {
456         CHECK_CLASS(tPtr, WC_TextField);
457
458         if ((text && strcmp(tPtr->text, text) == 0) || (!text && tPtr->textLen == 0))
459                 return;
460
461         if (text == NULL) {
462                 tPtr->text[0] = 0;
463                 tPtr->textLen = 0;
464         } else {
465                 tPtr->textLen = strlen(text);
466
467                 if (tPtr->textLen >= tPtr->bufferSize) {
468                         tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
469                         tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
470                 }
471                 strcpy(tPtr->text, text);
472         }
473
474         tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
475         tPtr->viewPosition = 0;
476         tPtr->selection.count = 0;
477
478         if (tPtr->view->flags.realized)
479                 paintTextField(tPtr);
480 }
481
482 void WMSetTextFieldAlignment(WMTextField * tPtr, WMAlignment alignment)
483 {
484         CHECK_CLASS(tPtr, WC_TextField);
485
486         tPtr->flags.alignment = alignment;
487
488         if (alignment != WALeft) {
489                 wwarning("only left alignment is supported in textfields");
490                 return;
491         }
492
493         if (tPtr->view->flags.realized) {
494                 paintTextField(tPtr);
495         }
496 }
497
498 void WMSetTextFieldBordered(WMTextField * tPtr, Bool bordered)
499 {
500         CHECK_CLASS(tPtr, WC_TextField);
501
502         tPtr->flags.bordered = bordered;
503
504         if (tPtr->view->flags.realized) {
505                 paintTextField(tPtr);
506         }
507 }
508
509 void WMSetTextFieldBeveled(WMTextField * tPtr, Bool flag)
510 {
511         CHECK_CLASS(tPtr, WC_TextField);
512
513         tPtr->flags.beveled = ((flag == 0) ? 0 : 1);
514
515         if (tPtr->view->flags.realized) {
516                 paintTextField(tPtr);
517         }
518 }
519
520 void WMSetTextFieldSecure(WMTextField * tPtr, Bool flag)
521 {
522         CHECK_CLASS(tPtr, WC_TextField);
523
524         tPtr->flags.secure = ((flag == 0) ? 0 : 1);
525
526         if (tPtr->view->flags.realized) {
527                 paintTextField(tPtr);
528         }
529 }
530
531 Bool WMGetTextFieldEditable(WMTextField * tPtr)
532 {
533         CHECK_CLASS(tPtr, WC_TextField);
534
535         return tPtr->flags.enabled;
536 }
537
538 void WMSetTextFieldEditable(WMTextField * tPtr, Bool flag)
539 {
540         CHECK_CLASS(tPtr, WC_TextField);
541
542         tPtr->flags.enabled = ((flag == 0) ? 0 : 1);
543
544         if (tPtr->view->flags.realized) {
545                 paintTextField(tPtr);
546         }
547 }
548
549 void WMSelectTextFieldRange(WMTextField * tPtr, WMRange range)
550 {
551         CHECK_CLASS(tPtr, WC_TextField);
552
553         if (tPtr->flags.enabled) {
554                 normalizeRange(tPtr, &range);
555
556                 tPtr->selection = range;
557
558                 tPtr->cursorPosition = range.position + range.count;
559
560                 if (tPtr->view->flags.realized) {
561                         paintTextField(tPtr);
562                 }
563         }
564 }
565
566 void WMSetTextFieldCursorPosition(WMTextField * tPtr, unsigned int position)
567 {
568         CHECK_CLASS(tPtr, WC_TextField);
569
570         if (tPtr->flags.enabled) {
571                 if (position > tPtr->textLen)
572                         position = tPtr->textLen;
573
574                 tPtr->cursorPosition = position;
575                 if (tPtr->view->flags.realized) {
576                         paintTextField(tPtr);
577                 }
578         }
579 }
580
581 unsigned WMGetTextFieldCursorPosition(WMTextField *tPtr)
582 {
583         CHECK_CLASS(tPtr, WC_TextField);
584
585         return tPtr->cursorPosition;
586 }
587
588 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
589 {
590         CHECK_CLASS(tPtr, WC_TextField);
591         if (next == NULL) {
592                 if (tPtr->view->nextFocusChain)
593                         tPtr->view->nextFocusChain->prevFocusChain = NULL;
594                 tPtr->view->nextFocusChain = NULL;
595                 return;
596         }
597
598         CHECK_CLASS(next, WC_TextField);
599
600         if (tPtr->view->nextFocusChain)
601                 tPtr->view->nextFocusChain->prevFocusChain = NULL;
602         if (next->view->prevFocusChain)
603                 next->view->prevFocusChain->nextFocusChain = NULL;
604
605         tPtr->view->nextFocusChain = next->view;
606         next->view->prevFocusChain = tPtr->view;
607 }
608
609 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
610 {
611         CHECK_CLASS(tPtr, WC_TextField);
612         if (prev == NULL) {
613                 if (tPtr->view->prevFocusChain)
614                         tPtr->view->prevFocusChain->nextFocusChain = NULL;
615                 tPtr->view->prevFocusChain = NULL;
616                 return;
617         }
618
619         CHECK_CLASS(prev, WC_TextField);
620
621         if (tPtr->view->prevFocusChain)
622                 tPtr->view->prevFocusChain->nextFocusChain = NULL;
623         if (prev->view->nextFocusChain)
624                 prev->view->nextFocusChain->prevFocusChain = NULL;
625
626         tPtr->view->prevFocusChain = prev->view;
627         prev->view->nextFocusChain = tPtr->view;
628 }
629
630 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
631 {
632         CHECK_CLASS(tPtr, WC_TextField);
633
634         if (tPtr->font)
635                 WMReleaseFont(tPtr->font);
636         tPtr->font = WMRetainFont(font);
637
638         tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
639
640         if (tPtr->view->flags.realized) {
641                 paintTextField(tPtr);
642         }
643 }
644
645 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
646 {
647         return tPtr->font;
648 }
649
650 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
651 {
652         WMTextField *tPtr = (WMTextField *) view->self;
653
654         tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
655
656         tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth /*+ 2 */ ;
657 }
658
659 static char *makeHiddenString(int length)
660 {
661         char *data = wmalloc(length + 1);
662
663         memset(data, '*', length);
664         data[length] = '\0';
665
666         return data;
667 }
668
669 static void paintCursor(TextField * tPtr)
670 {
671         int cx;
672         WMScreen *screen = tPtr->view->screen;
673         int textWidth;
674         char *text;
675
676         if (tPtr->flags.secure)
677                 text = makeHiddenString(strlen(tPtr->text));
678         else
679                 text = tPtr->text;
680
681         cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
682
683         switch (tPtr->flags.alignment) {
684         case WARight:
685                 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
686                 if (textWidth < tPtr->usableWidth)
687                         cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
688                 else
689                         cx += tPtr->offsetWidth + 1;
690                 break;
691         case WALeft:
692                 cx += tPtr->offsetWidth + 1;
693                 break;
694         case WAJustified:
695                 /* not supported */
696         case WACenter:
697                 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
698                 if (textWidth < tPtr->usableWidth)
699                         cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
700                 else
701                         cx += tPtr->offsetWidth;
702                 break;
703         }
704         /*
705            XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
706            cx, tPtr->offsetWidth, 1,
707            tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
708            printf("%d %d\n",cx,tPtr->cursorPosition);
709          */
710
711         XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
712                   cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
713
714         W_SetPreeditPositon(tPtr->view, cx, 0);
715
716         if (tPtr->flags.secure) {
717                 wfree(text);
718         }
719 }
720
721 static void drawRelief(WMView * view, Bool beveled)
722 {
723         WMScreen *scr = view->screen;
724         Display *dpy = scr->display;
725         GC wgc;
726         GC lgc;
727         GC dgc;
728         int width = view->size.width;
729         int height = view->size.height;
730
731         dgc = WMColorGC(scr->darkGray);
732
733         if (!beveled) {
734                 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
735
736                 return;
737         }
738         wgc = WMColorGC(scr->white);
739         lgc = WMColorGC(scr->gray);
740
741         /* top left */
742         XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
743         XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
744
745         XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
746         XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
747
748         /* bottom right */
749         XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
750         XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
751
752         XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
753         XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
754 }
755
756 static void paintTextField(TextField * tPtr)
757 {
758         W_Screen *screen = tPtr->view->screen;
759         W_View *view = tPtr->view;
760         W_View viewbuffer;
761         int tx, ty, tw, th;
762         int rx;
763         int bd;
764         int totalWidth;
765         char *text;
766         Pixmap drawbuffer;
767         WMColor *color;
768
769         if (!view->flags.realized || !view->flags.mapped)
770                 return;
771
772         if (!tPtr->flags.bordered) {
773                 bd = 0;
774         } else {
775                 bd = 2;
776         }
777
778         if (tPtr->flags.secure) {
779                 text = makeHiddenString(strlen(tPtr->text));
780         } else {
781                 text = tPtr->text;
782         }
783
784         totalWidth = tPtr->view->size.width - 2 * bd;
785
786         drawbuffer = XCreatePixmap(screen->display, view->window,
787                                    view->size.width, view->size.height, screen->depth);
788         XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
789                        0, 0, view->size.width, view->size.height);
790         /* this is quite dirty */
791         viewbuffer.screen = view->screen;
792         viewbuffer.size = view->size;
793         viewbuffer.window = drawbuffer;
794
795         if (tPtr->textLen > 0) {
796                 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
797
798                 th = WMFontHeight(tPtr->font);
799
800                 ty = tPtr->offsetWidth;
801                 switch (tPtr->flags.alignment) {
802                 case WALeft:
803                         tx = tPtr->offsetWidth + 1;
804                         if (tw < tPtr->usableWidth)
805                                 XFillRectangle(screen->display, drawbuffer,
806                                                WMColorGC(screen->white),
807                                                bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
808                         break;
809
810                 case WACenter:
811                         tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
812                         if (tw < tPtr->usableWidth)
813                                 XClearArea(screen->display, view->window, bd, bd,
814                                            totalWidth, view->size.height - 2 * bd, False);
815                         break;
816
817                 default:
818                 case WARight:
819                         tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
820                         if (tw < tPtr->usableWidth)
821                                 XClearArea(screen->display, view->window, bd, bd,
822                                            totalWidth - tw, view->size.height - 2 * bd, False);
823                         break;
824                 }
825
826                 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
827
828                 WMDrawImageString(screen, drawbuffer, color, screen->white,
829                                   tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
830                                   tPtr->textLen - tPtr->viewPosition);
831
832                 if (tPtr->selection.count) {
833                         int count, count2;
834
835                         count = tPtr->selection.count < 0
836                             ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
837                         count2 = abs(tPtr->selection.count);
838                         if (count < tPtr->viewPosition) {
839                                 count2 = abs(count2 - abs(tPtr->viewPosition - count));
840                                 count = tPtr->viewPosition;
841                         }
842
843                         rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
844                             - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
845
846                         WMDrawImageString(screen, drawbuffer, color, screen->gray,
847                                           tPtr->font, rx, ty, &(text[count]), count2);
848                 }
849         } else {
850                 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
851                                bd, bd, totalWidth, view->size.height - 2 * bd);
852         }
853
854         /* draw relief */
855         if (tPtr->flags.bordered) {
856                 drawRelief(&viewbuffer, tPtr->flags.beveled);
857         }
858
859         if (tPtr->flags.secure)
860                 wfree(text);
861         XCopyArea(screen->display, drawbuffer, view->window,
862                   screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
863         XFreePixmap(screen->display, drawbuffer);
864
865         /* draw cursor */
866         if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
867                 paintCursor(tPtr);
868         }
869 }
870
871 #if 0
872 static void blinkCursor(void *data)
873 {
874         TextField *tPtr = (TextField *) data;
875
876         if (tPtr->flags.cursorOn) {
877                 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor, data);
878         } else {
879                 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, data);
880         }
881         paintCursor(tPtr);
882         tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
883 }
884 #endif
885
886 static void handleEvents(XEvent * event, void *data)
887 {
888         TextField *tPtr = (TextField *) data;
889
890         CHECK_CLASS(data, WC_TextField);
891
892         switch (event->type) {
893         case FocusIn:
894                 W_FocusIC(tPtr->view);
895                 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
896                         return;
897                 tPtr->flags.focused = 1;
898 #if 0
899                 if (!tPtr->timerID) {
900                         tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
901                 }
902 #endif
903                 paintTextField(tPtr);
904
905                 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
906
907                 tPtr->flags.notIllegalMovement = 0;
908                 break;
909
910         case FocusOut:
911                 W_UnFocusIC(tPtr->view);
912                 tPtr->flags.focused = 0;
913 #if 0
914                 if (tPtr->timerID)
915                         WMDeleteTimerHandler(tPtr->timerID);
916                 tPtr->timerID = NULL;
917 #endif
918
919                 paintTextField(tPtr);
920                 if (!tPtr->flags.notIllegalMovement) {
921                         NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
922                                (void *)WMIllegalTextMovement);
923                 }
924                 break;
925
926         case Expose:
927                 if (event->xexpose.count != 0)
928                         break;
929                 paintTextField(tPtr);
930                 break;
931
932         case DestroyNotify:
933                 destroyTextField(tPtr);
934                 break;
935         }
936 }
937
938 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
939 {
940         char buffer[64];
941         KeySym ksym;
942         char *textEvent = NULL;
943         void *data = NULL;
944         int count, refresh = 0;
945         int control_pressed = 0;
946         int cancelSelection = 1;
947         Bool shifted, controled, modified;
948         Bool relay = True;
949
950         /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
951         if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
952                 control_pressed = 1;
953
954         shifted = (event->xkey.state & ShiftMask ? True : False);
955         controled = (event->xkey.state & ControlMask ? True : False);
956         modified = shifted || controled;
957
958         count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
959         //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
960         buffer[count] = '\0';
961
962         switch (ksym) {
963         case XK_Tab:
964 #ifdef XK_ISO_Left_Tab
965         case XK_ISO_Left_Tab:
966 #endif
967                 if (!controled) {
968                         if (shifted) {
969                                 if (tPtr->view->prevFocusChain) {
970                                         W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
971                                                              tPtr->view->prevFocusChain);
972                                         tPtr->flags.notIllegalMovement = 1;
973                                 }
974                                 data = (void *)WMBacktabTextMovement;
975                         } else {
976                                 if (tPtr->view->nextFocusChain) {
977                                         W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
978                                                              tPtr->view->nextFocusChain);
979                                         tPtr->flags.notIllegalMovement = 1;
980                                 }
981                                 data = (void *)WMTabTextMovement;
982                         }
983                         textEvent = WMTextDidEndEditingNotification;
984
985                         cancelSelection = 0;
986
987                         relay = False;
988                 }
989                 break;
990
991         case XK_Escape:
992                 if (!modified) {
993                         data = (void *)WMEscapeTextMovement;
994                         textEvent = WMTextDidEndEditingNotification;
995
996                         relay = False;
997                 }
998                 break;
999
1000         case XK_Return:
1001                 if (!modified) {
1002                         data = (void *)WMReturnTextMovement;
1003                         textEvent = WMTextDidEndEditingNotification;
1004
1005                         relay = False;
1006                 }
1007                 break;
1008
1009         case WM_EMACSKEY_LEFT:
1010                 if (!control_pressed)
1011                         goto normal_key;
1012                 else
1013                         controled = False;
1014
1015 #ifdef XK_KP_Left
1016         case XK_KP_Left:
1017 #endif
1018         case XK_Left:
1019                 if (tPtr->cursorPosition > 0) {
1020                         int i;
1021                         paintCursor(tPtr);
1022
1023                         i = tPtr->cursorPosition;
1024                         i += oneUTF8CharBackward(&tPtr->text[i], i);
1025                         if (controled) {
1026                                 while (i > 0 && tPtr->text[i] != ' ')
1027                                         i--;
1028                                 while (i > 0 && tPtr->text[i] == ' ')
1029                                         i--;
1030
1031                                 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1032                         } else
1033                                 tPtr->cursorPosition = i;
1034
1035                         if (tPtr->cursorPosition < tPtr->viewPosition) {
1036                                 tPtr->viewPosition = tPtr->cursorPosition;
1037                                 refresh = 1;
1038                         } else
1039                                 paintCursor(tPtr);
1040                 }
1041                 if (shifted)
1042                         cancelSelection = 0;
1043
1044                 relay = False;
1045
1046                 break;
1047
1048         case WM_EMACSKEY_RIGHT:
1049                 if (!control_pressed)
1050                         goto normal_key;
1051                 else
1052                         controled = False;
1053
1054 #ifdef XK_KP_Right
1055         case XK_KP_Right:
1056 #endif
1057         case XK_Right:
1058                 if (tPtr->cursorPosition < tPtr->textLen) {
1059                         int i;
1060                         paintCursor(tPtr);
1061
1062                         i = tPtr->cursorPosition;
1063                         if (controled) {
1064                                 while (tPtr->text[i] && tPtr->text[i] != ' ')
1065                                         i++;
1066                                 while (tPtr->text[i] == ' ')
1067                                         i++;
1068                         } else {
1069                                 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1070                         }
1071                         tPtr->cursorPosition = i;
1072
1073                         refresh = incrToFit2(tPtr);
1074
1075                         if (!refresh)
1076                                 paintCursor(tPtr);
1077                 }
1078                 if (shifted)
1079                         cancelSelection = 0;
1080
1081                 relay = False;
1082
1083                 break;
1084
1085         case WM_EMACSKEY_HOME:
1086                 if (!control_pressed)
1087                         goto normal_key;
1088                 else
1089                         controled = False;
1090
1091 #ifdef XK_KP_Home
1092         case XK_KP_Home:
1093 #endif
1094         case XK_Home:
1095                 if (!controled) {
1096                         if (tPtr->cursorPosition > 0) {
1097                                 paintCursor(tPtr);
1098                                 tPtr->cursorPosition = 0;
1099                                 if (tPtr->viewPosition > 0) {
1100                                         tPtr->viewPosition = 0;
1101                                         refresh = 1;
1102                                 } else
1103                                         paintCursor(tPtr);
1104                         }
1105                         if (shifted)
1106                                 cancelSelection = 0;
1107
1108                         relay = False;
1109                 }
1110                 break;
1111
1112         case WM_EMACSKEY_END:
1113                 if (!control_pressed)
1114                         goto normal_key;
1115                 else
1116                         controled = False;
1117
1118 #ifdef XK_KP_End
1119         case XK_KP_End:
1120 #endif
1121         case XK_End:
1122                 if (!controled) {
1123                         if (tPtr->cursorPosition < tPtr->textLen) {
1124                                 paintCursor(tPtr);
1125                                 tPtr->cursorPosition = tPtr->textLen;
1126                                 tPtr->viewPosition = 0;
1127
1128                                 refresh = incrToFit(tPtr);
1129
1130                                 if (!refresh)
1131                                         paintCursor(tPtr);
1132                         }
1133                         if (shifted)
1134                                 cancelSelection = 0;
1135
1136                         relay = False;
1137                 }
1138                 break;
1139
1140         case WM_EMACSKEY_BS:
1141                 if (!control_pressed)
1142                         goto normal_key;
1143                 else
1144                         modified = False;
1145
1146         case XK_BackSpace:
1147                 if (!modified) {
1148                         if (tPtr->selection.count) {
1149                                 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1150                                 data = (void *)WMDeleteTextEvent;
1151                                 textEvent = WMTextDidChangeNotification;
1152                         } else if (tPtr->cursorPosition > 0) {
1153                                 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1154                                                             tPtr->cursorPosition);
1155                                 WMRange range;
1156                                 range.position = tPtr->cursorPosition + i;
1157                                 range.count = -i;
1158                                 WMDeleteTextFieldRange(tPtr, range);
1159                                 data = (void *)WMDeleteTextEvent;
1160                                 textEvent = WMTextDidChangeNotification;
1161                         }
1162
1163                         relay = False;
1164                 }
1165                 break;
1166
1167         case WM_EMACSKEY_DEL:
1168                 if (!control_pressed)
1169                         goto normal_key;
1170                 else
1171                         modified = False;
1172
1173 #ifdef XK_KP_Delete
1174         case XK_KP_Delete:
1175 #endif
1176         case XK_Delete:
1177                 if (!modified) {
1178                         if (tPtr->selection.count) {
1179                                 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1180                                 data = (void *)WMDeleteTextEvent;
1181                                 textEvent = WMTextDidChangeNotification;
1182                         } else if (tPtr->cursorPosition < tPtr->textLen) {
1183                                 WMRange range;
1184                                 range.position = tPtr->cursorPosition;
1185                                 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1186                                                                  tPtr->textLen - tPtr->cursorPosition);
1187                                 WMDeleteTextFieldRange(tPtr, range);
1188                                 data = (void *)WMDeleteTextEvent;
1189                                 textEvent = WMTextDidChangeNotification;
1190                         }
1191
1192                         relay = False;
1193                 }
1194                 break;
1195
1196  normal_key:
1197         default:
1198                 if (!controled) {
1199                         if (count > 0 && !iscntrl(buffer[0])) {
1200                                 if (tPtr->selection.count)
1201                                         WMDeleteTextFieldRange(tPtr, tPtr->selection);
1202                                 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1203                                 data = (void *)WMInsertTextEvent;
1204                                 textEvent = WMTextDidChangeNotification;
1205
1206                                 relay = False;
1207                         }
1208                 }
1209                 break;
1210         }
1211
1212         if (relay) {
1213                 WMRelayToNextResponder(W_VIEW(tPtr), event);
1214                 return;
1215         }
1216
1217         /* Do not allow text selection in secure text fields */
1218         if (cancelSelection || tPtr->flags.secure) {
1219                 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1220
1221                 if (tPtr->selection.count) {
1222                         tPtr->selection.count = 0;
1223                         refresh = 1;
1224                 }
1225                 tPtr->selection.position = tPtr->cursorPosition;
1226         } else {
1227                 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1228
1229                         tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1230
1231                         refresh = 1;
1232                 }
1233         }
1234
1235         /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1236
1237         if (textEvent) {
1238                 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1239
1240                 if (tPtr->delegate) {
1241                         if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1242                                 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1243
1244                         else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1245                                 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1246
1247                         else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1248                                 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1249                 }
1250
1251                 WMPostNotification(notif);
1252                 WMReleaseNotification(notif);
1253         }
1254
1255         if (refresh)
1256                 paintTextField(tPtr);
1257
1258         /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1259 }
1260
1261 static int pointToCursorPosition(TextField * tPtr, int x)
1262 {
1263         int a, b, pos, prev, tw;
1264
1265         if (tPtr->flags.bordered)
1266                 x -= 2;
1267
1268         if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1269                             tPtr->textLen - tPtr->viewPosition) <= x)
1270                 return tPtr->textLen;
1271
1272         a = tPtr->viewPosition;
1273         b = tPtr->textLen;
1274
1275         /* we halve the text until we get into a 10 byte vicinity of x */
1276         while (b - a > 10) {
1277                 pos = (a + b) / 2;
1278                 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1279                 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1280                 if (tw > x) {
1281                         b = pos;
1282                 } else if (tw < x) {
1283                         a = pos;
1284                 } else {
1285                         return pos;
1286                 }
1287         }
1288
1289         /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1290          * inclusive, with the exception of the left border of the 'a' glyph and
1291          * the right border or the 'b-1' glyph
1292          *
1293          * ( <--- range for x's position ---> )
1294          * a a+1 .......................... b-1 b
1295          */
1296         pos = prev = a;
1297         while (pos <= b) {
1298                 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1299                 if (tw > x) {
1300                         return prev;
1301                 } else if (pos == b) {
1302                         break;
1303                 }
1304                 prev = pos;
1305                 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1306         }
1307
1308         return b;
1309 }
1310
1311 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1312 {
1313         TextField *tPtr = (TextField *) view->self;
1314         char *str;
1315
1316         tPtr->flags.waitingSelection = 0;
1317
1318         if (data != NULL) {
1319                 str = (char *)WMDataBytes(data);
1320
1321                 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1322                 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1323         } else {
1324                 int n;
1325
1326                 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1327
1328                 if (str != NULL) {
1329                         str[n] = 0;
1330                         WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1331                         XFree(str);
1332                         NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1333                 }
1334         }
1335 }
1336
1337 static void handleTextFieldActionEvents(XEvent * event, void *data)
1338 {
1339         TextField *tPtr = (TextField *) data;
1340         static int move = 0;
1341         static Time lastButtonReleasedEvent = 0;
1342         static Time lastButtonReleasedEvent2 = 0;
1343         Display *dpy = event->xany.display;
1344
1345         CHECK_CLASS(data, WC_TextField);
1346
1347         switch (event->type) {
1348         case KeyPress:
1349                 if (tPtr->flags.waitingSelection) {
1350                         return;
1351                 }
1352                 if (tPtr->flags.enabled && tPtr->flags.focused) {
1353                         handleTextFieldKeyPress(tPtr, event);
1354                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1355                         tPtr->flags.pointerGrabbed = 1;
1356                 }
1357                 break;
1358
1359         case MotionNotify:
1360
1361                 if (tPtr->flags.pointerGrabbed) {
1362                         tPtr->flags.pointerGrabbed = 0;
1363                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1364                 }
1365                 if (tPtr->flags.waitingSelection) {
1366                         return;
1367                 }
1368
1369                 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1370
1371                         if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1372                                 if (WMWidthOfString(tPtr->font,
1373                                                     &(tPtr->text[tPtr->viewPosition]),
1374                                                     tPtr->cursorPosition - tPtr->viewPosition)
1375                                     > tPtr->usableWidth) {
1376                                         tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1377                                                                                  tPtr->textLen -
1378                                                                                  tPtr->viewPosition);
1379                                 }
1380                         } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1381                                 paintCursor(tPtr);
1382                                 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1383                                                                           tPtr->viewPosition);
1384                         }
1385
1386                         tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1387
1388                         /* Do not allow text selection in secure textfields */
1389                         if (tPtr->flags.secure) {
1390                                 tPtr->selection.position = tPtr->cursorPosition;
1391                         }
1392
1393                         tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1394
1395                         paintCursor(tPtr);
1396                         paintTextField(tPtr);
1397
1398                 }
1399                 break;
1400
1401         case ButtonPress:
1402                 if (tPtr->flags.pointerGrabbed) {
1403                         tPtr->flags.pointerGrabbed = 0;
1404                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1405                         break;
1406                 }
1407
1408                 if (tPtr->flags.waitingSelection) {
1409                         break;
1410                 }
1411
1412                 move = 1;
1413                 switch (tPtr->flags.alignment) {
1414                         int textWidth;
1415                 case WARight:
1416                         textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1417                         if (tPtr->flags.enabled && !tPtr->flags.focused) {
1418                                 WMSetFocusToWidget(tPtr);
1419                         }
1420                         if (tPtr->flags.focused) {
1421                                 tPtr->selection.position = tPtr->cursorPosition;
1422                                 tPtr->selection.count = 0;
1423                         }
1424                         if (textWidth < tPtr->usableWidth) {
1425                                 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1426                                                                              event->xbutton.x - tPtr->usableWidth
1427                                                                              + textWidth);
1428                         } else
1429                                 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1430
1431                         paintTextField(tPtr);
1432                         break;
1433
1434                 case WALeft:
1435                         if (tPtr->flags.enabled && !tPtr->flags.focused) {
1436                                 WMSetFocusToWidget(tPtr);
1437                         }
1438                         if (tPtr->flags.focused && event->xbutton.button == Button1) {
1439                                 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1440                                 tPtr->selection.position = tPtr->cursorPosition;
1441                                 tPtr->selection.count = 0;
1442                                 paintTextField(tPtr);
1443                         }
1444                         if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1445                                 char *text;
1446                                 int n;
1447
1448                                 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1449                                                         event->xbutton.time, pasteText, NULL)) {
1450                                         text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1451
1452                                         if (text) {
1453                                                 text[n] = 0;
1454                                                 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1455                                                 XFree(text);
1456                                                 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1457                                                        (void *)WMInsertTextEvent);
1458                                         }
1459                                 } else {
1460                                         tPtr->flags.waitingSelection = 1;
1461                                 }
1462                         }
1463                         break;
1464                 default:
1465                         break;
1466                 }
1467                 break;
1468
1469         case ButtonRelease:
1470                 if (tPtr->flags.pointerGrabbed) {
1471                         tPtr->flags.pointerGrabbed = 0;
1472                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1473                 }
1474                 if (tPtr->flags.waitingSelection) {
1475                         break;
1476                 }
1477
1478                 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1479                         int start, count;
1480                         XRotateBuffers(dpy, 1);
1481
1482                         count = abs(tPtr->selection.count);
1483                         if (tPtr->selection.count < 0)
1484                                 start = tPtr->selection.position - count;
1485                         else
1486                                 start = tPtr->selection.position;
1487
1488                         XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1489                 }
1490
1491                 move = 0;
1492
1493                 if (!tPtr->flags.secure &&
1494                     event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1495
1496                         if (event->xbutton.time - lastButtonReleasedEvent2 <=
1497                             2 * WINGsConfiguration.doubleClickDelay) {
1498                                 tPtr->selection.position = 0;
1499                                 tPtr->selection.count = tPtr->textLen;
1500                         } else {
1501                                 int pos, cnt;
1502                                 char *txt;
1503                                 pos = tPtr->selection.position;
1504                                 cnt = tPtr->selection.count;
1505                                 txt = tPtr->text;
1506                                 while (pos >= 0) {
1507                                         if (txt[pos] == ' ' || txt[pos] == '\t')
1508                                                 break;
1509                                         pos--;
1510                                 }
1511                                 pos++;
1512
1513                                 while (pos + cnt < tPtr->textLen) {
1514                                         if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1515                                                 break;
1516                                         cnt++;
1517                                 }
1518                                 tPtr->selection.position = pos;
1519                                 tPtr->selection.count = cnt;
1520                         }
1521                         paintTextField(tPtr);
1522
1523                         if (!tPtr->flags.ownsSelection) {
1524                                 tPtr->flags.ownsSelection =
1525                                     WMCreateSelectionHandler(tPtr->view,
1526                                                              XA_PRIMARY,
1527                                                              event->xbutton.time, &selectionHandler, NULL);
1528                         }
1529                 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1530                         tPtr->flags.ownsSelection =
1531                             WMCreateSelectionHandler(tPtr->view,
1532                                                      XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1533                 }
1534
1535                 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1536                 lastButtonReleasedEvent = event->xbutton.time;
1537
1538                 break;
1539         }
1540 }
1541
1542 static void destroyTextField(TextField * tPtr)
1543 {
1544 #if 0
1545         if (tPtr->timerID)
1546                 WMDeleteTimerHandler(tPtr->timerID);
1547 #endif
1548
1549         W_DestroyIC(tPtr->view);
1550
1551         WMReleaseFont(tPtr->font);
1552         /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1553         WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1554         WMRemoveNotificationObserver(tPtr);
1555
1556         if (tPtr->text)
1557                 wfree(tPtr->text);
1558
1559         wfree(tPtr);
1560 }