8 #include <X11/keysym.h>
13 #define CURSOR_BLINK_ON_DELAY 600
14 #define CURSOR_BLINK_OFF_DELAY 300
18 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
19 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
20 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
23 typedef struct W_TextField
{
28 struct W_TextField
*nextField
; /* next textfield in the chain */
29 struct W_TextField
*prevField
;
33 int textLen
; /* size of text */
34 int bufferSize
; /* memory allocated for text */
36 int viewPosition
; /* position of text being shown */
38 int cursorPosition
; /* position of the insertion cursor */
41 short offsetWidth
; /* offset of text from border */
47 WMTextFieldDelegate
*delegate
;
50 WMHandlerID timerID
; /* for cursor blinking */
53 WMAlignment alignment
:2;
55 unsigned int bordered
:1;
57 unsigned int beveled
:1;
59 unsigned int enabled
:1;
61 unsigned int focused
:1;
63 unsigned int cursorOn
:1;
65 unsigned int secure
:1; /* password entry style */
67 unsigned int pointerGrabbed
:1;
69 unsigned int ownsSelection
:1;
71 unsigned int waitingSelection
:1; /* requested selection, but
75 unsigned int notIllegalMovement
:1;
80 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
81 if ((T)->delegate && (T)->delegate->C)\
82 (*(T)->delegate->C)((T)->delegate,notif);\
83 WMPostNotification(notif);\
84 WMReleaseNotification(notif);}
87 #define MIN_TEXT_BUFFER 2
88 #define TEXT_BUFFER_INCR 8
91 #define WM_EMACSKEYMASK ControlMask
93 #define WM_EMACSKEY_LEFT XK_b
94 #define WM_EMACSKEY_RIGHT XK_f
95 #define WM_EMACSKEY_HOME XK_a
96 #define WM_EMACSKEY_END XK_e
97 #define WM_EMACSKEY_BS XK_h
98 #define WM_EMACSKEY_DEL XK_d
102 #define DEFAULT_WIDTH 60
103 #define DEFAULT_HEIGHT 20
104 #define DEFAULT_BORDERED True
105 #define DEFAULT_ALIGNMENT WALeft
109 static void destroyTextField(TextField
*tPtr
);
110 static void paintTextField(TextField
*tPtr
);
112 static void handleEvents(XEvent
*event
, void *data
);
113 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
114 static void didResizeTextField();
116 struct W_ViewDelegate _TextFieldViewDelegate
= {
126 static void lostSelection(WMView
*view
, Atom selection
, void *cdata
);
128 static WMData
*requestHandler(WMView
*view
, Atom selection
, Atom target
,
129 void *cdata
, Atom
*type
);
132 static WMSelectionProcs selectionHandler
= {
139 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
140 &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
142 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
143 &((tPtr)->text[(start)]), (end) - (start)))
147 oneUTF8CharBackward(char *str
, int len
)
149 unsigned char* ustr
= (unsigned char*) str
;
152 while (len
-- > 0 && ustr
[--pos
] >= 0x80 && ustr
[pos
] <= 0xbf);
158 oneUTF8CharForward(char *str
, int len
)
160 unsigned char* ustr
= (unsigned char*) str
;
163 while (len
-- > 0 && ustr
[++pos
] >= 0x80 && ustr
[pos
] <= 0xbf);
168 // find the beginning of the UTF8 char pointed by str
170 seekUTF8CharStart(char *str
, int len
)
172 unsigned char* ustr
= (unsigned char*) str
;
175 while (len
-- > 0 && ustr
[pos
] >= 0x80 && ustr
[pos
] <= 0xbf) --pos
;
181 normalizeRange(TextField
*tPtr
, WMRange
*range
)
183 if (range
->position
< 0 && range
->count
< 0)
186 if (range
->count
== 0) {
187 /*range->position = 0; why is this?*/
191 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
192 if (range
->count
< 0) { /* && range->position >= 0 */
193 if (range
->position
+ range
->count
< 0) {
194 range
->count
= range
->position
;
197 range
->count
= -range
->count
;
198 range
->position
-= range
->count
;
200 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
201 } else if (range
->position
< 0) { /* && range->count > 0 */
202 if (range
->position
+ range
->count
< 0) {
203 range
->position
= range
->count
= 0;
205 range
->count
+= range
->position
;
210 if (range
->position
+ range
->count
> tPtr
->textLen
)
211 range
->count
= tPtr
->textLen
- range
->position
;
216 memmv(char *dest
, char *src
, int size
)
221 for (i
=size
-1; i
>=0; i
--) {
224 } else if (dest
< src
) {
225 for (i
=0; i
<size
; i
++) {
233 incrToFit(TextField
*tPtr
)
235 int vp
= tPtr
->viewPosition
;
237 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
238 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
239 tPtr
->textLen
- tPtr
->viewPosition
);
241 return vp
!=tPtr
->viewPosition
;
246 incrToFit2(TextField
*tPtr
)
248 int vp
= tPtr
->viewPosition
;
250 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
251 >= tPtr
->usableWidth
)
252 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
253 tPtr
->cursorPosition
- tPtr
->viewPosition
);
254 return vp
!=tPtr
->viewPosition
;
259 decrToFit(TextField
*tPtr
)
261 int vp
= tPtr
->viewPosition
;
263 while (vp
> 0 && (vp
+= oneUTF8CharBackward(&tPtr
->text
[vp
], vp
),
264 TEXT_WIDTH(tPtr
, vp
)) < tPtr
->usableWidth
) {
265 tPtr
->viewPosition
= vp
;
274 requestHandler(WMView
*view
, Atom selection
, Atom target
, void *cdata
,
277 TextField
*tPtr
= view
->self
;
279 Display
*dpy
= tPtr
->view
->screen
->display
;
281 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
282 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
285 count
= tPtr
->selection
.count
< 0
286 ? tPtr
->selection
.position
+ tPtr
->selection
.count
287 : tPtr
->selection
.position
;
289 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
291 data
= WMCreateDataWithBytes(&(tPtr
->text
[count
]),
292 abs(tPtr
->selection
.count
));
293 WMSetDataFormat(data
, 8);
299 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
300 if (target
== _TARGETS
) {
303 ptr
= wmalloc(4 * sizeof(Atom
));
307 ptr
[3] = COMPOUND_TEXT
;
309 data
= WMCreateDataWithBytes(ptr
, 4*4);
310 WMSetDataFormat(data
, 32);
322 lostSelection(WMView
*view
, Atom selection
, void *cdata
)
324 TextField
*tPtr
= (WMTextField
*)view
->self
;
326 if (tPtr
->flags
.ownsSelection
) {
327 WMDeleteSelectionHandler(view
, selection
, CurrentTime
);
328 tPtr
->flags
.ownsSelection
= 0;
330 if (tPtr
->selection
.count
!= 0) {
331 tPtr
->selection
.count
= 0;
332 paintTextField(tPtr
);
338 selectionNotification(void *observerData
, WMNotification
*notification
)
340 WMView
*observerView
= (WMView
*)observerData
;
341 WMView
*newOwnerView
= (WMView
*)WMGetNotificationClientData(notification
);
343 if (observerView
!= newOwnerView
) {
345 //if (tPtr->flags.ownsSelection)
346 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
348 lostSelection(observerView
, XA_PRIMARY
, NULL
);
354 realizeObserver(void *self
, WMNotification
*not)
356 W_CreateIC(((TextField
*)self
)->view
);
361 WMCreateTextField(WMWidget
*parent
)
365 tPtr
= wmalloc(sizeof(TextField
));
366 memset(tPtr
, 0, sizeof(TextField
));
368 tPtr
->widgetClass
= WC_TextField
;
370 tPtr
->view
= W_CreateView(W_VIEW(parent
));
375 tPtr
->view
->self
= tPtr
;
377 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
379 tPtr
->view
->attribFlags
|= CWCursor
;
380 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
382 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
384 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
387 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
389 tPtr
->flags
.enabled
= 1;
391 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
392 |FocusChangeMask
, handleEvents
, tPtr
);
394 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
396 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
397 tPtr
->flags
.beveled
= True
;
398 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
400 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
402 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
404 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
405 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
406 handleTextFieldActionEvents
, tPtr
);
408 WMAddNotificationObserver(selectionNotification
, tPtr
->view
,
409 WMSelectionOwnerDidChangeNotification
,
412 WMAddNotificationObserver(realizeObserver
, tPtr
,
413 WMViewRealizedNotification
, tPtr
->view
);
415 tPtr
->flags
.cursorOn
= 1;
422 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
424 CHECK_CLASS(tPtr
, WC_TextField
);
426 tPtr
->delegate
= delegate
;
431 WMGetTextFieldDelegate(WMTextField
*tPtr
)
433 CHECK_CLASS(tPtr
, WC_TextField
);
435 return tPtr
->delegate
;
440 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
444 CHECK_CLASS(tPtr
, WC_TextField
);
451 /* check if buffer will hold the text */
452 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
453 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
454 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
457 if (position
< 0 || position
>= tPtr
->textLen
) {
458 /* append the text at the end */
459 strcat(tPtr
->text
, text
);
460 tPtr
->textLen
+= len
;
461 tPtr
->cursorPosition
+= len
;
464 /* insert text at position */
465 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
466 tPtr
->textLen
-position
+1);
468 memcpy(&(tPtr
->text
[position
]), text
, len
);
470 tPtr
->textLen
+= len
;
471 if (position
>= tPtr
->cursorPosition
) {
472 tPtr
->cursorPosition
+= len
;
479 paintTextField(tPtr
);
483 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
485 CHECK_CLASS(tPtr
, WC_TextField
);
487 normalizeRange(tPtr
, &range
);
492 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
493 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
495 /* better than nothing ;) */
496 if (tPtr
->cursorPosition
> range
.position
)
497 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
],
499 tPtr
->textLen
-= range
.count
;
500 tPtr
->cursorPosition
= range
.position
;
504 paintTextField(tPtr
);
510 WMGetTextFieldText(WMTextField
*tPtr
)
512 CHECK_CLASS(tPtr
, WC_TextField
);
514 return wstrdup(tPtr
->text
);
519 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
521 CHECK_CLASS(tPtr
, WC_TextField
);
523 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
524 (!text
&& tPtr
->textLen
== 0))
531 tPtr
->textLen
= strlen(text
);
533 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
534 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
535 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
537 strcpy(tPtr
->text
, text
);
540 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
541 tPtr
->viewPosition
= 0;
542 tPtr
->selection
.count
= 0;
544 if (tPtr
->view
->flags
.realized
)
545 paintTextField(tPtr
);
550 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
552 CHECK_CLASS(tPtr
, WC_TextField
);
554 tPtr
->flags
.alignment
= alignment
;
556 if (alignment
!=WALeft
) {
557 wwarning("only left alignment is supported in textfields");
561 if (tPtr
->view
->flags
.realized
) {
562 paintTextField(tPtr
);
568 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
570 CHECK_CLASS(tPtr
, WC_TextField
);
572 tPtr
->flags
.bordered
= bordered
;
574 if (tPtr
->view
->flags
.realized
) {
575 paintTextField(tPtr
);
581 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
583 CHECK_CLASS(tPtr
, WC_TextField
);
585 tPtr
->flags
.beveled
= ((flag
==0) ? 0 : 1);
587 if (tPtr
->view
->flags
.realized
) {
588 paintTextField(tPtr
);
595 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
597 CHECK_CLASS(tPtr
, WC_TextField
);
599 tPtr
->flags
.secure
= ((flag
==0) ? 0 : 1);
601 if (tPtr
->view
->flags
.realized
) {
602 paintTextField(tPtr
);
608 WMGetTextFieldEditable(WMTextField
*tPtr
)
610 CHECK_CLASS(tPtr
, WC_TextField
);
612 return tPtr
->flags
.enabled
;
617 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
619 CHECK_CLASS(tPtr
, WC_TextField
);
621 tPtr
->flags
.enabled
= ((flag
==0) ? 0 : 1);
623 if (tPtr
->view
->flags
.realized
) {
624 paintTextField(tPtr
);
630 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
632 CHECK_CLASS(tPtr
, WC_TextField
);
634 if (tPtr
->flags
.enabled
) {
635 normalizeRange(tPtr
, &range
);
637 tPtr
->selection
= range
;
639 tPtr
->cursorPosition
= range
.position
+ range
.count
;
641 if (tPtr
->view
->flags
.realized
) {
642 paintTextField(tPtr
);
649 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
651 CHECK_CLASS(tPtr
, WC_TextField
);
653 if (tPtr
->flags
.enabled
) {
654 if (position
> tPtr
->textLen
)
655 position
= tPtr
->textLen
;
657 tPtr
->cursorPosition
= position
;
658 if (tPtr
->view
->flags
.realized
) {
659 paintTextField(tPtr
);
666 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
668 CHECK_CLASS(tPtr
, WC_TextField
);
670 if (tPtr
->view
->nextFocusChain
)
671 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
672 tPtr
->view
->nextFocusChain
= NULL
;
676 CHECK_CLASS(next
, WC_TextField
);
678 if (tPtr
->view
->nextFocusChain
)
679 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
680 if (next
->view
->prevFocusChain
)
681 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
683 tPtr
->view
->nextFocusChain
= next
->view
;
684 next
->view
->prevFocusChain
= tPtr
->view
;
689 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
691 CHECK_CLASS(tPtr
, WC_TextField
);
693 if (tPtr
->view
->prevFocusChain
)
694 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
695 tPtr
->view
->prevFocusChain
= NULL
;
699 CHECK_CLASS(prev
, WC_TextField
);
701 if (tPtr
->view
->prevFocusChain
)
702 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
703 if (prev
->view
->nextFocusChain
)
704 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
706 tPtr
->view
->prevFocusChain
= prev
->view
;
707 prev
->view
->nextFocusChain
= tPtr
->view
;
712 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
714 CHECK_CLASS(tPtr
, WC_TextField
);
717 WMReleaseFont(tPtr
->font
);
718 tPtr
->font
= WMRetainFont(font
);
721 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
723 if (tPtr
->view
->flags
.realized
) {
724 paintTextField(tPtr
);
731 WMGetTextFieldFont(WMTextField
*tPtr
)
738 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
740 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
743 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
745 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
/*+ 2*/;
750 makeHiddenString(int length
)
752 char *data
= wmalloc(length
+1);
754 memset(data
, '*', length
);
762 paintCursor(TextField
*tPtr
)
765 WMScreen
*screen
= tPtr
->view
->screen
;
769 if (tPtr
->flags
.secure
)
770 text
= makeHiddenString(strlen(tPtr
->text
));
774 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
775 tPtr
->cursorPosition
-tPtr
->viewPosition
);
777 switch (tPtr
->flags
.alignment
) {
779 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
780 if (textWidth
< tPtr
->usableWidth
)
781 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
783 cx
+= tPtr
->offsetWidth
+ 1;
786 cx
+= tPtr
->offsetWidth
+ 1;
791 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
792 if (textWidth
< tPtr
->usableWidth
)
793 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
795 cx
+= tPtr
->offsetWidth
;
799 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
800 cx, tPtr->offsetWidth, 1,
801 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
802 printf("%d %d\n",cx,tPtr->cursorPosition);
805 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
806 cx
, tPtr
->offsetWidth
, cx
,
807 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
809 W_SetPreeditPositon(tPtr
->view
, cx
, 0);
811 if (tPtr
->flags
.secure
) {
819 drawRelief(WMView
*view
, Bool beveled
)
821 WMScreen
*scr
= view
->screen
;
822 Display
*dpy
= scr
->display
;
826 int width
= view
->size
.width
;
827 int height
= view
->size
.height
;
829 dgc
= WMColorGC(scr
->darkGray
);
832 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
836 wgc
= WMColorGC(scr
->white
);
837 lgc
= WMColorGC(scr
->gray
);
840 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
841 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
843 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
844 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
847 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
848 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
850 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
851 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
856 paintTextField(TextField
*tPtr
)
858 W_Screen
*screen
= tPtr
->view
->screen
;
859 W_View
*view
= tPtr
->view
;
870 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
873 if (!tPtr
->flags
.bordered
) {
879 if (tPtr
->flags
.secure
) {
880 text
= makeHiddenString(strlen(tPtr
->text
));
885 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
887 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
888 view
->size
.width
, view
->size
.height
, screen
->depth
);
889 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
890 0,0, view
->size
.width
,view
->size
.height
);
891 /* this is quite dirty */
892 viewbuffer
.screen
= view
->screen
;
893 viewbuffer
.size
= view
->size
;
894 viewbuffer
.window
= drawbuffer
;
897 if (tPtr
->textLen
> 0) {
898 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
899 tPtr
->textLen
- tPtr
->viewPosition
);
901 th
= WMFontHeight(tPtr
->font
);
903 ty
= tPtr
->offsetWidth
;
904 switch (tPtr
->flags
.alignment
) {
906 tx
= tPtr
->offsetWidth
+ 1;
907 if (tw
< tPtr
->usableWidth
)
908 XFillRectangle(screen
->display
, drawbuffer
,
909 WMColorGC(screen
->white
),
910 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
914 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
915 if (tw
< tPtr
->usableWidth
)
916 XClearArea(screen
->display
, view
->window
, bd
, bd
,
917 totalWidth
, view
->size
.height
-2*bd
, False
);
922 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
923 if (tw
< tPtr
->usableWidth
)
924 XClearArea(screen
->display
, view
->window
, bd
, bd
,
925 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
929 color
= tPtr
->flags
.enabled
? screen
->black
: screen
->darkGray
;
931 WMDrawImageString(screen
, drawbuffer
, color
, screen
->white
,
932 tPtr
->font
, tx
, ty
, &(text
[tPtr
->viewPosition
]),
933 tPtr
->textLen
- tPtr
->viewPosition
);
935 if (tPtr
->selection
.count
) {
938 count
= tPtr
->selection
.count
< 0
939 ? tPtr
->selection
.position
+ tPtr
->selection
.count
940 : tPtr
->selection
.position
;
941 count2
= abs(tPtr
->selection
.count
);
942 if (count
< tPtr
->viewPosition
) {
943 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
944 count
= tPtr
->viewPosition
;
947 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
948 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
950 WMDrawImageString(screen
, drawbuffer
, color
, screen
->gray
,
951 tPtr
->font
, rx
, ty
, &(text
[count
]), count2
);
954 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
955 bd
, bd
, totalWidth
,view
->size
.height
-2*bd
);
959 if (tPtr
->flags
.bordered
) {
960 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
963 if (tPtr
->flags
.secure
)
965 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
966 screen
->copyGC
, 0,0, view
->size
.width
,
967 view
->size
.height
,0,0);
968 XFreePixmap(screen
->display
, drawbuffer
);
971 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
979 blinkCursor(void *data
)
981 TextField
*tPtr
= (TextField
*)data
;
983 if (tPtr
->flags
.cursorOn
) {
984 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
987 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
991 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
997 handleEvents(XEvent
*event
, void *data
)
999 TextField
*tPtr
= (TextField
*)data
;
1001 CHECK_CLASS(data
, WC_TextField
);
1003 switch (event
->type
) {
1005 W_FocusIC(tPtr
->view
);
1006 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
1008 tPtr
->flags
.focused
= 1;
1010 if (!tPtr
->timerID
) {
1011 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
1015 paintTextField(tPtr
);
1017 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
1019 tPtr
->flags
.notIllegalMovement
= 0;
1023 W_UnFocusIC(tPtr
->view
);
1024 tPtr
->flags
.focused
= 0;
1027 WMDeleteTimerHandler(tPtr
->timerID
);
1028 tPtr
->timerID
= NULL
;
1031 paintTextField(tPtr
);
1032 if (!tPtr
->flags
.notIllegalMovement
) {
1033 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
1034 (void*)WMIllegalTextMovement
);
1039 if (event
->xexpose
.count
!=0)
1041 paintTextField(tPtr
);
1045 destroyTextField(tPtr
);
1052 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
1056 char *textEvent
= NULL
;
1058 int count
, refresh
= 0;
1059 int control_pressed
= 0;
1060 int cancelSelection
= 1;
1061 Bool shifted
, controled
, modified
;
1064 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1065 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1066 control_pressed
= 1;
1068 shifted
= (event
->xkey
.state
& ShiftMask
? True
: False
);
1069 controled
= (event
->xkey
.state
& ControlMask
? True
: False
);
1070 modified
= shifted
|| controled
;
1072 count
= W_LookupString(tPtr
->view
, &event
->xkey
, buffer
, 63, &ksym
, NULL
);
1073 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
1074 buffer
[count
] = '\0';
1078 #ifdef XK_ISO_Left_Tab
1079 case XK_ISO_Left_Tab
:
1083 if (tPtr
->view
->prevFocusChain
) {
1084 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1085 tPtr
->view
->prevFocusChain
);
1086 tPtr
->flags
.notIllegalMovement
= 1;
1088 data
= (void*)WMBacktabTextMovement
;
1090 if (tPtr
->view
->nextFocusChain
) {
1091 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1092 tPtr
->view
->nextFocusChain
);
1093 tPtr
->flags
.notIllegalMovement
= 1;
1095 data
= (void*)WMTabTextMovement
;
1097 textEvent
= WMTextDidEndEditingNotification
;
1099 cancelSelection
= 0;
1107 data
= (void*)WMEscapeTextMovement
;
1108 textEvent
= WMTextDidEndEditingNotification
;
1116 data
= (void*)WMReturnTextMovement
;
1117 textEvent
= WMTextDidEndEditingNotification
;
1123 case WM_EMACSKEY_LEFT
:
1124 if (!control_pressed
)
1133 if (tPtr
->cursorPosition
> 0) {
1137 i
= tPtr
->cursorPosition
;
1138 i
+= oneUTF8CharBackward(&tPtr
->text
[i
], i
);
1140 while (i
> 0 && tPtr
->text
[i
] != ' ') i
--;
1141 while (i
> 0 && tPtr
->text
[i
] == ' ') i
--;
1143 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1145 tPtr
->cursorPosition
= i
;
1147 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1148 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1154 cancelSelection
= 0;
1160 case WM_EMACSKEY_RIGHT
:
1161 if (!control_pressed
)
1170 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1174 i
= tPtr
->cursorPosition
;
1176 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ') i
++;
1177 while (tPtr
->text
[i
] == ' ') i
++;
1179 i
+= oneUTF8CharForward(&tPtr
->text
[i
], tPtr
->textLen
- i
);
1181 tPtr
->cursorPosition
= i
;
1183 refresh
= incrToFit2(tPtr
);
1189 cancelSelection
= 0;
1195 case WM_EMACSKEY_HOME
:
1196 if (!control_pressed
)
1206 if (tPtr
->cursorPosition
> 0) {
1208 tPtr
->cursorPosition
= 0;
1209 if (tPtr
->viewPosition
> 0) {
1210 tPtr
->viewPosition
= 0;
1216 cancelSelection
= 0;
1222 case WM_EMACSKEY_END
:
1223 if (!control_pressed
)
1233 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1235 tPtr
->cursorPosition
= tPtr
->textLen
;
1236 tPtr
->viewPosition
= 0;
1238 refresh
= incrToFit(tPtr
);
1244 cancelSelection
= 0;
1250 case WM_EMACSKEY_BS
:
1251 if (!control_pressed
)
1258 if (tPtr
->selection
.count
) {
1259 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1260 data
= (void*)WMDeleteTextEvent
;
1261 textEvent
= WMTextDidChangeNotification
;
1262 } else if (tPtr
->cursorPosition
> 0) {
1263 int i
= oneUTF8CharBackward(&tPtr
->text
[tPtr
->cursorPosition
],
1264 tPtr
->cursorPosition
);
1266 range
.position
= tPtr
->cursorPosition
+ i
;
1268 WMDeleteTextFieldRange(tPtr
, range
);
1269 data
= (void*)WMDeleteTextEvent
;
1270 textEvent
= WMTextDidChangeNotification
;
1277 case WM_EMACSKEY_DEL
:
1278 if (!control_pressed
)
1288 if (tPtr
->selection
.count
) {
1289 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1290 data
= (void*)WMDeleteTextEvent
;
1291 textEvent
= WMTextDidChangeNotification
;
1292 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1294 range
.position
= tPtr
->cursorPosition
;
1295 range
.count
= oneUTF8CharForward(&tPtr
->text
[tPtr
->cursorPosition
],
1296 tPtr
->textLen
- tPtr
->cursorPosition
);
1297 WMDeleteTextFieldRange(tPtr
, range
);
1298 data
= (void*)WMDeleteTextEvent
;
1299 textEvent
= WMTextDidChangeNotification
;
1309 if (count
> 0 && !iscntrl(buffer
[0])) {
1310 if (tPtr
->selection
.count
)
1311 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1312 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1313 data
= (void*)WMInsertTextEvent
;
1314 textEvent
= WMTextDidChangeNotification
;
1323 WMRelayToNextResponder(W_VIEW(tPtr
), event
);
1327 /* Do not allow text selection in secure text fields */
1328 if (cancelSelection
|| tPtr
->flags
.secure
) {
1329 lostSelection(tPtr
->view
, XA_PRIMARY
, NULL
);
1331 if (tPtr
->selection
.count
) {
1332 tPtr
->selection
.count
= 0;
1335 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1337 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1339 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1345 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1348 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1350 if (tPtr
->delegate
) {
1351 if (textEvent
==WMTextDidBeginEditingNotification
&&
1352 tPtr
->delegate
->didBeginEditing
)
1353 (*tPtr
->delegate
->didBeginEditing
)(tPtr
->delegate
, notif
);
1355 else if (textEvent
==WMTextDidEndEditingNotification
&&
1356 tPtr
->delegate
->didEndEditing
)
1357 (*tPtr
->delegate
->didEndEditing
)(tPtr
->delegate
, notif
);
1359 else if (textEvent
==WMTextDidChangeNotification
&&
1360 tPtr
->delegate
->didChange
)
1361 (*tPtr
->delegate
->didChange
)(tPtr
->delegate
, notif
);
1364 WMPostNotification(notif
);
1365 WMReleaseNotification(notif
);
1369 paintTextField(tPtr
);
1371 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1376 pointToCursorPosition(TextField
*tPtr
, int x
)
1381 if (tPtr
->flags
.bordered
)
1384 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1385 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1386 return tPtr
->textLen
;
1388 a
= tPtr
->viewPosition
;
1393 mid
+= seekUTF8CharStart(&tPtr
->text
[mid
], mid
- a
);
1394 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1395 mid
- tPtr
->viewPosition
+ 1);
1398 } else if (tw
< x
) {
1400 a
+= oneUTF8CharForward(&tPtr
->text
[mid
], b
- a
);
1412 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1413 void *cdata
, WMData
*data
)
1415 TextField
*tPtr
= (TextField
*)view
->self
;
1418 tPtr
->flags
.waitingSelection
= 0;
1421 str
= (char*)WMDataBytes(data
);
1423 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1424 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1425 (void*)WMInsertTextEvent
);
1429 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1433 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1435 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1436 (void*)WMInsertTextEvent
);
1443 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1445 TextField
*tPtr
= (TextField
*)data
;
1446 static int move
= 0;
1447 static Time lastButtonReleasedEvent
= 0;
1448 static Time lastButtonReleasedEvent2
= 0;
1449 Display
*dpy
= event
->xany
.display
;
1451 CHECK_CLASS(data
, WC_TextField
);
1453 switch (event
->type
) {
1455 if (tPtr
->flags
.waitingSelection
) {
1458 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1459 handleTextFieldKeyPress(tPtr
, event
);
1460 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1461 W_VIEW(tPtr
)->screen
->invisibleCursor
);
1462 tPtr
->flags
.pointerGrabbed
= 1;
1468 if (tPtr
->flags
.pointerGrabbed
) {
1469 tPtr
->flags
.pointerGrabbed
= 0;
1470 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1471 W_VIEW(tPtr
)->screen
->textCursor
);
1473 if (tPtr
->flags
.waitingSelection
) {
1477 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1479 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1480 tPtr
->usableWidth
) {
1481 if (WMWidthOfString(tPtr
->font
,
1482 &(tPtr
->text
[tPtr
->viewPosition
]),
1483 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1484 > tPtr
->usableWidth
) {
1485 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
1486 tPtr
->textLen
- tPtr
->viewPosition
);
1488 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1490 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
],
1491 tPtr
->viewPosition
);
1494 tPtr
->cursorPosition
=
1495 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1497 /* Do not allow text selection in secure textfields */
1498 if (tPtr
->flags
.secure
) {
1499 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1502 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1505 paintTextField(tPtr
);
1511 if (tPtr
->flags
.pointerGrabbed
) {
1512 tPtr
->flags
.pointerGrabbed
= 0;
1513 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1514 W_VIEW(tPtr
)->screen
->textCursor
);
1518 if (tPtr
->flags
.waitingSelection
) {
1523 switch (tPtr
->flags
.alignment
) {
1526 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1527 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1528 WMSetFocusToWidget(tPtr
);
1530 if (tPtr
->flags
.focused
) {
1531 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1532 tPtr
->selection
.count
= 0;
1534 if (textWidth
< tPtr
->usableWidth
) {
1535 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1536 event
->xbutton
.x
- tPtr
->usableWidth
1538 } else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1541 paintTextField(tPtr
);
1545 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1546 WMSetFocusToWidget(tPtr
);
1548 if (tPtr
->flags
.focused
&& event
->xbutton
.button
== Button1
) {
1549 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1551 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1552 tPtr
->selection
.count
= 0;
1553 paintTextField(tPtr
);
1555 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1559 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1560 event
->xbutton
.time
,
1562 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1566 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1568 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1569 (void*)WMInsertTextEvent
);
1572 tPtr
->flags
.waitingSelection
= 1;
1582 if (tPtr
->flags
.pointerGrabbed
) {
1583 tPtr
->flags
.pointerGrabbed
= 0;
1584 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1585 W_VIEW(tPtr
)->screen
->textCursor
);
1587 if (tPtr
->flags
.waitingSelection
) {
1591 if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!=0) {
1593 XRotateBuffers(dpy
, 1);
1595 count
= abs(tPtr
->selection
.count
);
1596 if (tPtr
->selection
.count
< 0)
1597 start
= tPtr
->selection
.position
- count
;
1599 start
= tPtr
->selection
.position
;
1601 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1606 if (!tPtr
->flags
.secure
&&
1607 event
->xbutton
.time
- lastButtonReleasedEvent
1608 <= WINGsConfiguration
.doubleClickDelay
) {
1610 if (event
->xbutton
.time
- lastButtonReleasedEvent2
<= 2*WINGsConfiguration
.doubleClickDelay
) {
1611 tPtr
->selection
.position
= 0;
1612 tPtr
->selection
.count
= tPtr
->textLen
;
1616 pos
= tPtr
->selection
.position
;
1617 cnt
= tPtr
->selection
.count
;
1620 if (txt
[pos
] == ' ' || txt
[pos
] == '\t') break;
1625 while(pos
+ cnt
< tPtr
->textLen
) {
1626 if (txt
[pos
+ cnt
] == ' ' || txt
[pos
+ cnt
] == '\t')
1630 tPtr
->selection
.position
= pos
;
1631 tPtr
->selection
.count
= cnt
;
1633 paintTextField(tPtr
);
1635 if (!tPtr
->flags
.ownsSelection
) {
1636 tPtr
->flags
.ownsSelection
=
1637 WMCreateSelectionHandler(tPtr
->view
,
1639 event
->xbutton
.time
,
1640 &selectionHandler
, NULL
);
1642 } else if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!=0 &&
1643 !tPtr
->flags
.ownsSelection
) {
1644 tPtr
->flags
.ownsSelection
=
1645 WMCreateSelectionHandler(tPtr
->view
,
1647 event
->xbutton
.time
,
1648 &selectionHandler
, NULL
);
1651 lastButtonReleasedEvent2
= lastButtonReleasedEvent
;
1652 lastButtonReleasedEvent
= event
->xbutton
.time
;
1660 destroyTextField(TextField
*tPtr
)
1664 WMDeleteTimerHandler(tPtr
->timerID
);
1667 W_DestroyIC(tPtr
->view
);
1669 WMReleaseFont(tPtr
->font
);
1670 /*// use lostSelection() instead of WMDeleteSelectionHandler here?*/
1671 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1672 WMRemoveNotificationObserver(tPtr
);