Change to the linux kernel coding style
[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 void WMSetTextFieldNextTextField(WMTextField * tPtr, WMTextField * next)
582 {
583         CHECK_CLASS(tPtr, WC_TextField);
584         if (next == NULL) {
585                 if (tPtr->view->nextFocusChain)
586                         tPtr->view->nextFocusChain->prevFocusChain = NULL;
587                 tPtr->view->nextFocusChain = NULL;
588                 return;
589         }
590
591         CHECK_CLASS(next, WC_TextField);
592
593         if (tPtr->view->nextFocusChain)
594                 tPtr->view->nextFocusChain->prevFocusChain = NULL;
595         if (next->view->prevFocusChain)
596                 next->view->prevFocusChain->nextFocusChain = NULL;
597
598         tPtr->view->nextFocusChain = next->view;
599         next->view->prevFocusChain = tPtr->view;
600 }
601
602 void WMSetTextFieldPrevTextField(WMTextField * tPtr, WMTextField * prev)
603 {
604         CHECK_CLASS(tPtr, WC_TextField);
605         if (prev == NULL) {
606                 if (tPtr->view->prevFocusChain)
607                         tPtr->view->prevFocusChain->nextFocusChain = NULL;
608                 tPtr->view->prevFocusChain = NULL;
609                 return;
610         }
611
612         CHECK_CLASS(prev, WC_TextField);
613
614         if (tPtr->view->prevFocusChain)
615                 tPtr->view->prevFocusChain->nextFocusChain = NULL;
616         if (prev->view->nextFocusChain)
617                 prev->view->nextFocusChain->prevFocusChain = NULL;
618
619         tPtr->view->prevFocusChain = prev->view;
620         prev->view->nextFocusChain = tPtr->view;
621 }
622
623 void WMSetTextFieldFont(WMTextField * tPtr, WMFont * font)
624 {
625         CHECK_CLASS(tPtr, WC_TextField);
626
627         if (tPtr->font)
628                 WMReleaseFont(tPtr->font);
629         tPtr->font = WMRetainFont(font);
630
631         tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
632
633         if (tPtr->view->flags.realized) {
634                 paintTextField(tPtr);
635         }
636 }
637
638 WMFont *WMGetTextFieldFont(WMTextField * tPtr)
639 {
640         return tPtr->font;
641 }
642
643 static void didResizeTextField(W_ViewDelegate * self, WMView * view)
644 {
645         WMTextField *tPtr = (WMTextField *) view->self;
646
647         tPtr->offsetWidth = WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font)) / 2, 1);
648
649         tPtr->usableWidth = tPtr->view->size.width - 2 * tPtr->offsetWidth /*+ 2 */ ;
650 }
651
652 static char *makeHiddenString(int length)
653 {
654         char *data = wmalloc(length + 1);
655
656         memset(data, '*', length);
657         data[length] = '\0';
658
659         return data;
660 }
661
662 static void paintCursor(TextField * tPtr)
663 {
664         int cx;
665         WMScreen *screen = tPtr->view->screen;
666         int textWidth;
667         char *text;
668
669         if (tPtr->flags.secure)
670                 text = makeHiddenString(strlen(tPtr->text));
671         else
672                 text = tPtr->text;
673
674         cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->cursorPosition - tPtr->viewPosition);
675
676         switch (tPtr->flags.alignment) {
677         case WARight:
678                 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
679                 if (textWidth < tPtr->usableWidth)
680                         cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
681                 else
682                         cx += tPtr->offsetWidth + 1;
683                 break;
684         case WALeft:
685                 cx += tPtr->offsetWidth + 1;
686                 break;
687         case WAJustified:
688                 /* not supported */
689         case WACenter:
690                 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
691                 if (textWidth < tPtr->usableWidth)
692                         cx += tPtr->offsetWidth + (tPtr->usableWidth - textWidth) / 2;
693                 else
694                         cx += tPtr->offsetWidth;
695                 break;
696         }
697         /*
698            XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
699            cx, tPtr->offsetWidth, 1,
700            tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
701            printf("%d %d\n",cx,tPtr->cursorPosition);
702          */
703
704         XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
705                   cx, tPtr->offsetWidth, cx, tPtr->view->size.height - tPtr->offsetWidth - 1);
706
707         W_SetPreeditPositon(tPtr->view, cx, 0);
708
709         if (tPtr->flags.secure) {
710                 wfree(text);
711         }
712 }
713
714 static void drawRelief(WMView * view, Bool beveled)
715 {
716         WMScreen *scr = view->screen;
717         Display *dpy = scr->display;
718         GC wgc;
719         GC lgc;
720         GC dgc;
721         int width = view->size.width;
722         int height = view->size.height;
723
724         dgc = WMColorGC(scr->darkGray);
725
726         if (!beveled) {
727                 XDrawRectangle(dpy, view->window, dgc, 0, 0, width - 1, height - 1);
728
729                 return;
730         }
731         wgc = WMColorGC(scr->white);
732         lgc = WMColorGC(scr->gray);
733
734         /* top left */
735         XDrawLine(dpy, view->window, dgc, 0, 0, width - 1, 0);
736         XDrawLine(dpy, view->window, dgc, 0, 1, width - 2, 1);
737
738         XDrawLine(dpy, view->window, dgc, 0, 0, 0, height - 2);
739         XDrawLine(dpy, view->window, dgc, 1, 0, 1, height - 3);
740
741         /* bottom right */
742         XDrawLine(dpy, view->window, wgc, 0, height - 1, width - 1, height - 1);
743         XDrawLine(dpy, view->window, lgc, 1, height - 2, width - 2, height - 2);
744
745         XDrawLine(dpy, view->window, wgc, width - 1, 0, width - 1, height - 1);
746         XDrawLine(dpy, view->window, lgc, width - 2, 1, width - 2, height - 3);
747 }
748
749 static void paintTextField(TextField * tPtr)
750 {
751         W_Screen *screen = tPtr->view->screen;
752         W_View *view = tPtr->view;
753         W_View viewbuffer;
754         int tx, ty, tw, th;
755         int rx;
756         int bd;
757         int totalWidth;
758         char *text;
759         Pixmap drawbuffer;
760         WMColor *color;
761
762         if (!view->flags.realized || !view->flags.mapped)
763                 return;
764
765         if (!tPtr->flags.bordered) {
766                 bd = 0;
767         } else {
768                 bd = 2;
769         }
770
771         if (tPtr->flags.secure) {
772                 text = makeHiddenString(strlen(tPtr->text));
773         } else {
774                 text = tPtr->text;
775         }
776
777         totalWidth = tPtr->view->size.width - 2 * bd;
778
779         drawbuffer = XCreatePixmap(screen->display, view->window,
780                                    view->size.width, view->size.height, screen->depth);
781         XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
782                        0, 0, view->size.width, view->size.height);
783         /* this is quite dirty */
784         viewbuffer.screen = view->screen;
785         viewbuffer.size = view->size;
786         viewbuffer.window = drawbuffer;
787
788         if (tPtr->textLen > 0) {
789                 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]), tPtr->textLen - tPtr->viewPosition);
790
791                 th = WMFontHeight(tPtr->font);
792
793                 ty = tPtr->offsetWidth;
794                 switch (tPtr->flags.alignment) {
795                 case WALeft:
796                         tx = tPtr->offsetWidth + 1;
797                         if (tw < tPtr->usableWidth)
798                                 XFillRectangle(screen->display, drawbuffer,
799                                                WMColorGC(screen->white),
800                                                bd + tw, bd, totalWidth - tw, view->size.height - 2 * bd);
801                         break;
802
803                 case WACenter:
804                         tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
805                         if (tw < tPtr->usableWidth)
806                                 XClearArea(screen->display, view->window, bd, bd,
807                                            totalWidth, view->size.height - 2 * bd, False);
808                         break;
809
810                 default:
811                 case WARight:
812                         tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
813                         if (tw < tPtr->usableWidth)
814                                 XClearArea(screen->display, view->window, bd, bd,
815                                            totalWidth - tw, view->size.height - 2 * bd, False);
816                         break;
817                 }
818
819                 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
820
821                 WMDrawImageString(screen, drawbuffer, color, screen->white,
822                                   tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
823                                   tPtr->textLen - tPtr->viewPosition);
824
825                 if (tPtr->selection.count) {
826                         int count, count2;
827
828                         count = tPtr->selection.count < 0
829                             ? tPtr->selection.position + tPtr->selection.count : tPtr->selection.position;
830                         count2 = abs(tPtr->selection.count);
831                         if (count < tPtr->viewPosition) {
832                                 count2 = abs(count2 - abs(tPtr->viewPosition - count));
833                                 count = tPtr->viewPosition;
834                         }
835
836                         rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font, text, count)
837                             - WMWidthOfString(tPtr->font, text, tPtr->viewPosition);
838
839                         WMDrawImageString(screen, drawbuffer, color, screen->gray,
840                                           tPtr->font, rx, ty, &(text[count]), count2);
841                 }
842         } else {
843                 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
844                                bd, bd, totalWidth, view->size.height - 2 * bd);
845         }
846
847         /* draw relief */
848         if (tPtr->flags.bordered) {
849                 drawRelief(&viewbuffer, tPtr->flags.beveled);
850         }
851
852         if (tPtr->flags.secure)
853                 wfree(text);
854         XCopyArea(screen->display, drawbuffer, view->window,
855                   screen->copyGC, 0, 0, view->size.width, view->size.height, 0, 0);
856         XFreePixmap(screen->display, drawbuffer);
857
858         /* draw cursor */
859         if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
860                 paintCursor(tPtr);
861         }
862 }
863
864 #if 0
865 static void blinkCursor(void *data)
866 {
867         TextField *tPtr = (TextField *) data;
868
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);
873         }
874         paintCursor(tPtr);
875         tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
876 }
877 #endif
878
879 static void handleEvents(XEvent * event, void *data)
880 {
881         TextField *tPtr = (TextField *) data;
882
883         CHECK_CLASS(data, WC_TextField);
884
885         switch (event->type) {
886         case FocusIn:
887                 W_FocusIC(tPtr->view);
888                 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view)) != tPtr->view)
889                         return;
890                 tPtr->flags.focused = 1;
891 #if 0
892                 if (!tPtr->timerID) {
893                         tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor, tPtr);
894                 }
895 #endif
896                 paintTextField(tPtr);
897
898                 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
899
900                 tPtr->flags.notIllegalMovement = 0;
901                 break;
902
903         case FocusOut:
904                 W_UnFocusIC(tPtr->view);
905                 tPtr->flags.focused = 0;
906 #if 0
907                 if (tPtr->timerID)
908                         WMDeleteTimerHandler(tPtr->timerID);
909                 tPtr->timerID = NULL;
910 #endif
911
912                 paintTextField(tPtr);
913                 if (!tPtr->flags.notIllegalMovement) {
914                         NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
915                                (void *)WMIllegalTextMovement);
916                 }
917                 break;
918
919         case Expose:
920                 if (event->xexpose.count != 0)
921                         break;
922                 paintTextField(tPtr);
923                 break;
924
925         case DestroyNotify:
926                 destroyTextField(tPtr);
927                 break;
928         }
929 }
930
931 static void handleTextFieldKeyPress(TextField * tPtr, XEvent * event)
932 {
933         char buffer[64];
934         KeySym ksym;
935         char *textEvent = NULL;
936         void *data = NULL;
937         int count, refresh = 0;
938         int control_pressed = 0;
939         int cancelSelection = 1;
940         Bool shifted, controled, modified;
941         Bool relay = True;
942
943         /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
944         if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
945                 control_pressed = 1;
946
947         shifted = (event->xkey.state & ShiftMask ? True : False);
948         controled = (event->xkey.state & ControlMask ? True : False);
949         modified = shifted || controled;
950
951         count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
952         //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
953         buffer[count] = '\0';
954
955         switch (ksym) {
956         case XK_Tab:
957 #ifdef XK_ISO_Left_Tab
958         case XK_ISO_Left_Tab:
959 #endif
960                 if (!controled) {
961                         if (shifted) {
962                                 if (tPtr->view->prevFocusChain) {
963                                         W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
964                                                              tPtr->view->prevFocusChain);
965                                         tPtr->flags.notIllegalMovement = 1;
966                                 }
967                                 data = (void *)WMBacktabTextMovement;
968                         } else {
969                                 if (tPtr->view->nextFocusChain) {
970                                         W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
971                                                              tPtr->view->nextFocusChain);
972                                         tPtr->flags.notIllegalMovement = 1;
973                                 }
974                                 data = (void *)WMTabTextMovement;
975                         }
976                         textEvent = WMTextDidEndEditingNotification;
977
978                         cancelSelection = 0;
979
980                         relay = False;
981                 }
982                 break;
983
984         case XK_Escape:
985                 if (!modified) {
986                         data = (void *)WMEscapeTextMovement;
987                         textEvent = WMTextDidEndEditingNotification;
988
989                         relay = False;
990                 }
991                 break;
992
993         case XK_Return:
994                 if (!modified) {
995                         data = (void *)WMReturnTextMovement;
996                         textEvent = WMTextDidEndEditingNotification;
997
998                         relay = False;
999                 }
1000                 break;
1001
1002         case WM_EMACSKEY_LEFT:
1003                 if (!control_pressed)
1004                         goto normal_key;
1005                 else
1006                         controled = False;
1007
1008 #ifdef XK_KP_Left
1009         case XK_KP_Left:
1010 #endif
1011         case XK_Left:
1012                 if (tPtr->cursorPosition > 0) {
1013                         int i;
1014                         paintCursor(tPtr);
1015
1016                         i = tPtr->cursorPosition;
1017                         i += oneUTF8CharBackward(&tPtr->text[i], i);
1018                         if (controled) {
1019                                 while (i > 0 && tPtr->text[i] != ' ')
1020                                         i--;
1021                                 while (i > 0 && tPtr->text[i] == ' ')
1022                                         i--;
1023
1024                                 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1025                         } else
1026                                 tPtr->cursorPosition = i;
1027
1028                         if (tPtr->cursorPosition < tPtr->viewPosition) {
1029                                 tPtr->viewPosition = tPtr->cursorPosition;
1030                                 refresh = 1;
1031                         } else
1032                                 paintCursor(tPtr);
1033                 }
1034                 if (shifted)
1035                         cancelSelection = 0;
1036
1037                 relay = False;
1038
1039                 break;
1040
1041         case WM_EMACSKEY_RIGHT:
1042                 if (!control_pressed)
1043                         goto normal_key;
1044                 else
1045                         controled = False;
1046
1047 #ifdef XK_KP_Right
1048         case XK_KP_Right:
1049 #endif
1050         case XK_Right:
1051                 if (tPtr->cursorPosition < tPtr->textLen) {
1052                         int i;
1053                         paintCursor(tPtr);
1054
1055                         i = tPtr->cursorPosition;
1056                         if (controled) {
1057                                 while (tPtr->text[i] && tPtr->text[i] != ' ')
1058                                         i++;
1059                                 while (tPtr->text[i] == ' ')
1060                                         i++;
1061                         } else {
1062                                 i += oneUTF8CharForward(&tPtr->text[i], tPtr->textLen - i);
1063                         }
1064                         tPtr->cursorPosition = i;
1065
1066                         refresh = incrToFit2(tPtr);
1067
1068                         if (!refresh)
1069                                 paintCursor(tPtr);
1070                 }
1071                 if (shifted)
1072                         cancelSelection = 0;
1073
1074                 relay = False;
1075
1076                 break;
1077
1078         case WM_EMACSKEY_HOME:
1079                 if (!control_pressed)
1080                         goto normal_key;
1081                 else
1082                         controled = False;
1083
1084 #ifdef XK_KP_Home
1085         case XK_KP_Home:
1086 #endif
1087         case XK_Home:
1088                 if (!controled) {
1089                         if (tPtr->cursorPosition > 0) {
1090                                 paintCursor(tPtr);
1091                                 tPtr->cursorPosition = 0;
1092                                 if (tPtr->viewPosition > 0) {
1093                                         tPtr->viewPosition = 0;
1094                                         refresh = 1;
1095                                 } else
1096                                         paintCursor(tPtr);
1097                         }
1098                         if (shifted)
1099                                 cancelSelection = 0;
1100
1101                         relay = False;
1102                 }
1103                 break;
1104
1105         case WM_EMACSKEY_END:
1106                 if (!control_pressed)
1107                         goto normal_key;
1108                 else
1109                         controled = False;
1110
1111 #ifdef XK_KP_End
1112         case XK_KP_End:
1113 #endif
1114         case XK_End:
1115                 if (!controled) {
1116                         if (tPtr->cursorPosition < tPtr->textLen) {
1117                                 paintCursor(tPtr);
1118                                 tPtr->cursorPosition = tPtr->textLen;
1119                                 tPtr->viewPosition = 0;
1120
1121                                 refresh = incrToFit(tPtr);
1122
1123                                 if (!refresh)
1124                                         paintCursor(tPtr);
1125                         }
1126                         if (shifted)
1127                                 cancelSelection = 0;
1128
1129                         relay = False;
1130                 }
1131                 break;
1132
1133         case WM_EMACSKEY_BS:
1134                 if (!control_pressed)
1135                         goto normal_key;
1136                 else
1137                         modified = False;
1138
1139         case XK_BackSpace:
1140                 if (!modified) {
1141                         if (tPtr->selection.count) {
1142                                 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1143                                 data = (void *)WMDeleteTextEvent;
1144                                 textEvent = WMTextDidChangeNotification;
1145                         } else if (tPtr->cursorPosition > 0) {
1146                                 int i = oneUTF8CharBackward(&tPtr->text[tPtr->cursorPosition],
1147                                                             tPtr->cursorPosition);
1148                                 WMRange range;
1149                                 range.position = tPtr->cursorPosition + i;
1150                                 range.count = -i;
1151                                 WMDeleteTextFieldRange(tPtr, range);
1152                                 data = (void *)WMDeleteTextEvent;
1153                                 textEvent = WMTextDidChangeNotification;
1154                         }
1155
1156                         relay = False;
1157                 }
1158                 break;
1159
1160         case WM_EMACSKEY_DEL:
1161                 if (!control_pressed)
1162                         goto normal_key;
1163                 else
1164                         modified = False;
1165
1166 #ifdef XK_KP_Delete
1167         case XK_KP_Delete:
1168 #endif
1169         case XK_Delete:
1170                 if (!modified) {
1171                         if (tPtr->selection.count) {
1172                                 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1173                                 data = (void *)WMDeleteTextEvent;
1174                                 textEvent = WMTextDidChangeNotification;
1175                         } else if (tPtr->cursorPosition < tPtr->textLen) {
1176                                 WMRange range;
1177                                 range.position = tPtr->cursorPosition;
1178                                 range.count = oneUTF8CharForward(&tPtr->text[tPtr->cursorPosition],
1179                                                                  tPtr->textLen - tPtr->cursorPosition);
1180                                 WMDeleteTextFieldRange(tPtr, range);
1181                                 data = (void *)WMDeleteTextEvent;
1182                                 textEvent = WMTextDidChangeNotification;
1183                         }
1184
1185                         relay = False;
1186                 }
1187                 break;
1188
1189  normal_key:
1190         default:
1191                 if (!controled) {
1192                         if (count > 0 && !iscntrl(buffer[0])) {
1193                                 if (tPtr->selection.count)
1194                                         WMDeleteTextFieldRange(tPtr, tPtr->selection);
1195                                 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1196                                 data = (void *)WMInsertTextEvent;
1197                                 textEvent = WMTextDidChangeNotification;
1198
1199                                 relay = False;
1200                         }
1201                 }
1202                 break;
1203         }
1204
1205         if (relay) {
1206                 WMRelayToNextResponder(W_VIEW(tPtr), event);
1207                 return;
1208         }
1209
1210         /* Do not allow text selection in secure text fields */
1211         if (cancelSelection || tPtr->flags.secure) {
1212                 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1213
1214                 if (tPtr->selection.count) {
1215                         tPtr->selection.count = 0;
1216                         refresh = 1;
1217                 }
1218                 tPtr->selection.position = tPtr->cursorPosition;
1219         } else {
1220                 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1221
1222                         tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1223
1224                         refresh = 1;
1225                 }
1226         }
1227
1228         /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1229
1230         if (textEvent) {
1231                 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1232
1233                 if (tPtr->delegate) {
1234                         if (textEvent == WMTextDidBeginEditingNotification && tPtr->delegate->didBeginEditing)
1235                                 (*tPtr->delegate->didBeginEditing) (tPtr->delegate, notif);
1236
1237                         else if (textEvent == WMTextDidEndEditingNotification && tPtr->delegate->didEndEditing)
1238                                 (*tPtr->delegate->didEndEditing) (tPtr->delegate, notif);
1239
1240                         else if (textEvent == WMTextDidChangeNotification && tPtr->delegate->didChange)
1241                                 (*tPtr->delegate->didChange) (tPtr->delegate, notif);
1242                 }
1243
1244                 WMPostNotification(notif);
1245                 WMReleaseNotification(notif);
1246         }
1247
1248         if (refresh)
1249                 paintTextField(tPtr);
1250
1251         /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1252 }
1253
1254 static int pointToCursorPosition(TextField * tPtr, int x)
1255 {
1256         int a, b, pos, prev, tw;
1257
1258         if (tPtr->flags.bordered)
1259                 x -= 2;
1260
1261         if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1262                             tPtr->textLen - tPtr->viewPosition) <= x)
1263                 return tPtr->textLen;
1264
1265         a = tPtr->viewPosition;
1266         b = tPtr->textLen;
1267
1268         /* we halve the text until we get into a 10 byte vicinity of x */
1269         while (b - a > 10) {
1270                 pos = (a + b) / 2;
1271                 pos += seekUTF8CharStart(&tPtr->text[pos], pos - a);
1272                 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1273                 if (tw > x) {
1274                         b = pos;
1275                 } else if (tw < x) {
1276                         a = pos;
1277                 } else {
1278                         return pos;
1279                 }
1280         }
1281
1282         /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1283          * inclusive, with the exception of the left border of the 'a' glyph and
1284          * the right border or the 'b-1' glyph
1285          *
1286          * ( <--- range for x's position ---> )
1287          * a a+1 .......................... b-1 b
1288          */
1289         pos = prev = a;
1290         while (pos <= b) {
1291                 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]), pos - tPtr->viewPosition);
1292                 if (tw > x) {
1293                         return prev;
1294                 } else if (pos == b) {
1295                         break;
1296                 }
1297                 prev = pos;
1298                 pos += oneUTF8CharForward(&tPtr->text[pos], b - pos);
1299         }
1300
1301         return b;
1302 }
1303
1304 static void pasteText(WMView * view, Atom selection, Atom target, Time timestamp, void *cdata, WMData * data)
1305 {
1306         TextField *tPtr = (TextField *) view->self;
1307         char *str;
1308
1309         tPtr->flags.waitingSelection = 0;
1310
1311         if (data != NULL) {
1312                 str = (char *)WMDataBytes(data);
1313
1314                 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1315                 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1316         } else {
1317                 int n;
1318
1319                 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1320
1321                 if (str != NULL) {
1322                         str[n] = 0;
1323                         WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1324                         XFree(str);
1325                         NOTIFY(tPtr, didChange, WMTextDidChangeNotification, (void *)WMInsertTextEvent);
1326                 }
1327         }
1328 }
1329
1330 static void handleTextFieldActionEvents(XEvent * event, void *data)
1331 {
1332         TextField *tPtr = (TextField *) data;
1333         static int move = 0;
1334         static Time lastButtonReleasedEvent = 0;
1335         static Time lastButtonReleasedEvent2 = 0;
1336         Display *dpy = event->xany.display;
1337
1338         CHECK_CLASS(data, WC_TextField);
1339
1340         switch (event->type) {
1341         case KeyPress:
1342                 if (tPtr->flags.waitingSelection) {
1343                         return;
1344                 }
1345                 if (tPtr->flags.enabled && tPtr->flags.focused) {
1346                         handleTextFieldKeyPress(tPtr, event);
1347                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->invisibleCursor);
1348                         tPtr->flags.pointerGrabbed = 1;
1349                 }
1350                 break;
1351
1352         case MotionNotify:
1353
1354                 if (tPtr->flags.pointerGrabbed) {
1355                         tPtr->flags.pointerGrabbed = 0;
1356                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1357                 }
1358                 if (tPtr->flags.waitingSelection) {
1359                         return;
1360                 }
1361
1362                 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1363
1364                         if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x > tPtr->usableWidth) {
1365                                 if (WMWidthOfString(tPtr->font,
1366                                                     &(tPtr->text[tPtr->viewPosition]),
1367                                                     tPtr->cursorPosition - tPtr->viewPosition)
1368                                     > tPtr->usableWidth) {
1369                                         tPtr->viewPosition += oneUTF8CharForward(&tPtr->text[tPtr->viewPosition],
1370                                                                                  tPtr->textLen -
1371                                                                                  tPtr->viewPosition);
1372                                 }
1373                         } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1374                                 paintCursor(tPtr);
1375                                 tPtr->viewPosition += oneUTF8CharBackward(&tPtr->text[tPtr->viewPosition],
1376                                                                           tPtr->viewPosition);
1377                         }
1378
1379                         tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1380
1381                         /* Do not allow text selection in secure textfields */
1382                         if (tPtr->flags.secure) {
1383                                 tPtr->selection.position = tPtr->cursorPosition;
1384                         }
1385
1386                         tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1387
1388                         paintCursor(tPtr);
1389                         paintTextField(tPtr);
1390
1391                 }
1392                 break;
1393
1394         case ButtonPress:
1395                 if (tPtr->flags.pointerGrabbed) {
1396                         tPtr->flags.pointerGrabbed = 0;
1397                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1398                         break;
1399                 }
1400
1401                 if (tPtr->flags.waitingSelection) {
1402                         break;
1403                 }
1404
1405                 move = 1;
1406                 switch (tPtr->flags.alignment) {
1407                         int textWidth;
1408                 case WARight:
1409                         textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1410                         if (tPtr->flags.enabled && !tPtr->flags.focused) {
1411                                 WMSetFocusToWidget(tPtr);
1412                         }
1413                         if (tPtr->flags.focused) {
1414                                 tPtr->selection.position = tPtr->cursorPosition;
1415                                 tPtr->selection.count = 0;
1416                         }
1417                         if (textWidth < tPtr->usableWidth) {
1418                                 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1419                                                                              event->xbutton.x - tPtr->usableWidth
1420                                                                              + textWidth);
1421                         } else
1422                                 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1423
1424                         paintTextField(tPtr);
1425                         break;
1426
1427                 case WALeft:
1428                         if (tPtr->flags.enabled && !tPtr->flags.focused) {
1429                                 WMSetFocusToWidget(tPtr);
1430                         }
1431                         if (tPtr->flags.focused && event->xbutton.button == Button1) {
1432                                 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xbutton.x);
1433                                 tPtr->selection.position = tPtr->cursorPosition;
1434                                 tPtr->selection.count = 0;
1435                                 paintTextField(tPtr);
1436                         }
1437                         if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1438                                 char *text;
1439                                 int n;
1440
1441                                 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1442                                                         event->xbutton.time, pasteText, NULL)) {
1443                                         text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1444
1445                                         if (text) {
1446                                                 text[n] = 0;
1447                                                 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1448                                                 XFree(text);
1449                                                 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1450                                                        (void *)WMInsertTextEvent);
1451                                         }
1452                                 } else {
1453                                         tPtr->flags.waitingSelection = 1;
1454                                 }
1455                         }
1456                         break;
1457                 default:
1458                         break;
1459                 }
1460                 break;
1461
1462         case ButtonRelease:
1463                 if (tPtr->flags.pointerGrabbed) {
1464                         tPtr->flags.pointerGrabbed = 0;
1465                         XDefineCursor(dpy, W_VIEW(tPtr)->window, W_VIEW(tPtr)->screen->textCursor);
1466                 }
1467                 if (tPtr->flags.waitingSelection) {
1468                         break;
1469                 }
1470
1471                 if (!tPtr->flags.secure && tPtr->selection.count != 0) {
1472                         int start, count;
1473                         XRotateBuffers(dpy, 1);
1474
1475                         count = abs(tPtr->selection.count);
1476                         if (tPtr->selection.count < 0)
1477                                 start = tPtr->selection.position - count;
1478                         else
1479                                 start = tPtr->selection.position;
1480
1481                         XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1482                 }
1483
1484                 move = 0;
1485
1486                 if (!tPtr->flags.secure &&
1487                     event->xbutton.time - lastButtonReleasedEvent <= WINGsConfiguration.doubleClickDelay) {
1488
1489                         if (event->xbutton.time - lastButtonReleasedEvent2 <=
1490                             2 * WINGsConfiguration.doubleClickDelay) {
1491                                 tPtr->selection.position = 0;
1492                                 tPtr->selection.count = tPtr->textLen;
1493                         } else {
1494                                 int pos, cnt;
1495                                 char *txt;
1496                                 pos = tPtr->selection.position;
1497                                 cnt = tPtr->selection.count;
1498                                 txt = tPtr->text;
1499                                 while (pos >= 0) {
1500                                         if (txt[pos] == ' ' || txt[pos] == '\t')
1501                                                 break;
1502                                         pos--;
1503                                 }
1504                                 pos++;
1505
1506                                 while (pos + cnt < tPtr->textLen) {
1507                                         if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1508                                                 break;
1509                                         cnt++;
1510                                 }
1511                                 tPtr->selection.position = pos;
1512                                 tPtr->selection.count = cnt;
1513                         }
1514                         paintTextField(tPtr);
1515
1516                         if (!tPtr->flags.ownsSelection) {
1517                                 tPtr->flags.ownsSelection =
1518                                     WMCreateSelectionHandler(tPtr->view,
1519                                                              XA_PRIMARY,
1520                                                              event->xbutton.time, &selectionHandler, NULL);
1521                         }
1522                 } else if (!tPtr->flags.secure && tPtr->selection.count != 0 && !tPtr->flags.ownsSelection) {
1523                         tPtr->flags.ownsSelection =
1524                             WMCreateSelectionHandler(tPtr->view,
1525                                                      XA_PRIMARY, event->xbutton.time, &selectionHandler, NULL);
1526                 }
1527
1528                 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1529                 lastButtonReleasedEvent = event->xbutton.time;
1530
1531                 break;
1532         }
1533 }
1534
1535 static void destroyTextField(TextField * tPtr)
1536 {
1537 #if 0
1538         if (tPtr->timerID)
1539                 WMDeleteTimerHandler(tPtr->timerID);
1540 #endif
1541
1542         W_DestroyIC(tPtr->view);
1543
1544         WMReleaseFont(tPtr->font);
1545         /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1546         WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1547         WMRemoveNotificationObserver(tPtr);
1548
1549         if (tPtr->text)
1550                 wfree(tPtr->text);
1551
1552         wfree(tPtr);
1553 }