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 }