5 #include <X11/keysym.h>
10 #define CURSOR_BLINK_ON_DELAY 600
11 #define CURSOR_BLINK_OFF_DELAY 300
13 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
14 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
15 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
17 typedef struct W_TextField
{
22 struct W_TextField
*nextField
; /* next textfield in the chain */
23 struct W_TextField
*prevField
;
27 int textLen
; /* size of text */
28 int bufferSize
; /* memory allocated for text */
30 int viewPosition
; /* position of text being shown */
32 int cursorPosition
; /* position of the insertion cursor */
35 short offsetWidth
; /* offset of text from border */
41 WMTextFieldDelegate
*delegate
;
43 WMHandlerID timerID
; /* for cursor blinking */
46 WMAlignment alignment
:2;
48 unsigned int bordered
:1;
50 unsigned int beveled
:1;
52 unsigned int enabled
:1;
54 unsigned int focused
:1;
56 unsigned int cursorOn
:1;
58 unsigned int secure
:1; /* password entry style */
60 unsigned int pointerGrabbed
:1;
62 unsigned int ownsSelection
:1;
64 unsigned int waitingSelection
:1; /* requested selection, but
67 unsigned int notIllegalMovement
:1;
71 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
72 if ((T)->delegate && (T)->delegate->C)\
73 (*(T)->delegate->C)((T)->delegate,notif);\
74 WMPostNotification(notif);\
75 WMReleaseNotification(notif);}
77 #define MIN_TEXT_BUFFER 2
78 #define TEXT_BUFFER_INCR 8
80 #define WM_EMACSKEYMASK ControlMask
82 #define WM_EMACSKEY_LEFT XK_b
83 #define WM_EMACSKEY_RIGHT XK_f
84 #define WM_EMACSKEY_HOME XK_a
85 #define WM_EMACSKEY_END XK_e
86 #define WM_EMACSKEY_BS XK_h
87 #define WM_EMACSKEY_DEL XK_d
89 #define DEFAULT_WIDTH 60
90 #define DEFAULT_HEIGHT 20
91 #define DEFAULT_BORDERED True
92 #define DEFAULT_ALIGNMENT WALeft
94 static void destroyTextField(TextField
* tPtr
);
95 static void paintTextField(TextField
* tPtr
);
97 static void handleEvents(XEvent
* event
, void *data
);
98 static void handleTextFieldActionEvents(XEvent
* event
, void *data
);
99 static void didResizeTextField(W_ViewDelegate
* self
, WMView
* view
);
101 struct W_ViewDelegate _TextFieldViewDelegate
= {
109 static void lostSelection(WMView
* view
, Atom selection
, void *cdata
);
111 static WMData
*requestHandler(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
);
113 static WMSelectionProcs selectionHandler
= {
119 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
120 &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
122 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
123 &((tPtr)->text[(start)]), (end) - (start)))
125 static inline int oneUTF8CharBackward(const char *str
, int len
)
127 const unsigned char *ustr
= (const unsigned char *)str
;
130 while (len
-- > 0 && ustr
[--pos
] >= 0x80 && ustr
[pos
] <= 0xbf) ;
134 static inline int oneUTF8CharForward(const char *str
, int len
)
136 const unsigned char *ustr
= (const unsigned char *)str
;
139 while (len
-- > 0 && ustr
[++pos
] >= 0x80 && ustr
[pos
] <= 0xbf) ;
143 // find the beginning of the UTF8 char pointed by str
144 static inline int seekUTF8CharStart(const char *str
, int len
)
146 const unsigned char *ustr
= (const unsigned char *)str
;
149 while (len
-- > 0 && ustr
[pos
] >= 0x80 && ustr
[pos
] <= 0xbf)
154 static void normalizeRange(TextField
* tPtr
, WMRange
* range
)
156 if (range
->position
< 0 && range
->count
< 0)
159 if (range
->count
== 0) {
160 /*range->position = 0; why is this? */
164 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
165 if (range
->count
< 0) { /* && range->position >= 0 */
166 if (range
->position
+ range
->count
< 0) {
167 range
->count
= range
->position
;
170 range
->count
= -range
->count
;
171 range
->position
-= range
->count
;
173 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
174 } else if (range
->position
< 0) { /* && range->count > 0 */
175 if (range
->position
+ range
->count
< 0) {
176 range
->position
= range
->count
= 0;
178 range
->count
+= range
->position
;
183 if (range
->position
+ range
->count
> tPtr
->textLen
)
184 range
->count
= tPtr
->textLen
- range
->position
;
187 static void memmv(char *dest
, const char *src
, int size
)
192 for (i
= size
- 1; i
>= 0; i
--) {
195 } else if (dest
< src
) {
196 for (i
= 0; i
< size
; i
++) {
202 static int incrToFit(TextField
* tPtr
)
204 int vp
= tPtr
->viewPosition
;
206 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
207 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
208 tPtr
->textLen
- tPtr
->viewPosition
);
210 return vp
!= tPtr
->viewPosition
;
213 static int incrToFit2(TextField
* tPtr
)
215 int vp
= tPtr
->viewPosition
;
217 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
218 >= tPtr
->usableWidth
)
219 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
220 tPtr
->cursorPosition
- tPtr
->viewPosition
);
221 return vp
!= tPtr
->viewPosition
;
224 static void decrToFit(TextField
* tPtr
)
226 int vp
= tPtr
->viewPosition
;
228 while (vp
> 0 && (vp
+= oneUTF8CharBackward(&tPtr
->text
[vp
], vp
),
229 TEXT_WIDTH(tPtr
, vp
)) < tPtr
->usableWidth
) {
230 tPtr
->viewPosition
= vp
;
237 static WMData
*requestHandler(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
)
239 TextField
*tPtr
= view
->self
;
241 Display
*dpy
= tPtr
->view
->screen
->display
;
243 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
244 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
247 /* Parameter not used, but tell the compiler that it is ok */
251 count
= tPtr
->selection
.count
< 0
252 ? tPtr
->selection
.position
+ tPtr
->selection
.count
: tPtr
->selection
.position
;
254 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
256 data
= WMCreateDataWithBytes(&(tPtr
->text
[count
]), abs(tPtr
->selection
.count
));
257 WMSetDataFormat(data
, 8);
263 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
264 if (target
== _TARGETS
) {
265 Atom supported_type
[4];
267 supported_type
[0] = _TARGETS
;
268 supported_type
[1] = XA_STRING
;
269 supported_type
[2] = TEXT
;
270 supported_type
[3] = COMPOUND_TEXT
;
272 data
= WMCreateDataWithBytes(supported_type
, sizeof(supported_type
));
273 WMSetDataFormat(data
, 32);
283 static void lostSelection(WMView
* view
, Atom selection
, void *cdata
)
285 TextField
*tPtr
= (WMTextField
*) view
->self
;
287 /* Parameter not used, but tell the compiler that it is ok */
290 if (tPtr
->flags
.ownsSelection
) {
291 WMDeleteSelectionHandler(view
, selection
, CurrentTime
);
292 tPtr
->flags
.ownsSelection
= 0;
294 if (tPtr
->selection
.count
!= 0) {
295 tPtr
->selection
.count
= 0;
296 paintTextField(tPtr
);
300 static void selectionNotification(void *observerData
, WMNotification
* notification
)
302 WMView
*observerView
= (WMView
*) observerData
;
303 WMView
*newOwnerView
= (WMView
*) WMGetNotificationClientData(notification
);
305 if (observerView
!= newOwnerView
) {
307 //if (tPtr->flags.ownsSelection)
308 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
310 lostSelection(observerView
, XA_PRIMARY
, NULL
);
314 static void realizeObserver(void *self
, WMNotification
* not)
316 /* Parameter not used, but tell the compiler that it is ok */
319 W_CreateIC(((TextField
*) self
)->view
);
322 WMTextField
*WMCreateTextField(WMWidget
* parent
)
326 tPtr
= wmalloc(sizeof(TextField
));
327 tPtr
->widgetClass
= WC_TextField
;
329 tPtr
->view
= W_CreateView(W_VIEW(parent
));
334 tPtr
->view
->self
= tPtr
;
336 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
338 tPtr
->view
->attribFlags
|= CWCursor
;
339 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
341 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
343 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
345 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
347 tPtr
->flags
.enabled
= 1;
349 WMCreateEventHandler(tPtr
->view
, ExposureMask
| StructureNotifyMask
| FocusChangeMask
, handleEvents
, tPtr
);
351 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
353 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
354 tPtr
->flags
.beveled
= True
;
355 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
356 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
358 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
360 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
| LeaveWindowMask
361 | ButtonReleaseMask
| ButtonPressMask
| KeyPressMask
| Button1MotionMask
,
362 handleTextFieldActionEvents
, tPtr
);
364 WMAddNotificationObserver(selectionNotification
, tPtr
->view
,
365 WMSelectionOwnerDidChangeNotification
, (void *)XA_PRIMARY
);
367 WMAddNotificationObserver(realizeObserver
, tPtr
, WMViewRealizedNotification
, tPtr
->view
);
369 tPtr
->flags
.cursorOn
= 1;
374 void WMSetTextFieldDelegate(WMTextField
* tPtr
, WMTextFieldDelegate
* delegate
)
376 CHECK_CLASS(tPtr
, WC_TextField
);
378 tPtr
->delegate
= delegate
;
381 WMTextFieldDelegate
*WMGetTextFieldDelegate(WMTextField
* tPtr
)
383 CHECK_CLASS(tPtr
, WC_TextField
);
385 return tPtr
->delegate
;
388 void WMInsertTextFieldText(WMTextField
* tPtr
, const char *text
, int position
)
392 CHECK_CLASS(tPtr
, WC_TextField
);
399 /* check if buffer will hold the text */
400 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
401 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
402 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
405 if (position
< 0 || position
>= tPtr
->textLen
) {
406 /* append the text at the end */
407 wstrlcat(tPtr
->text
, text
, tPtr
->bufferSize
);
408 tPtr
->textLen
+= len
;
409 tPtr
->cursorPosition
+= len
;
412 /* insert text at position */
413 memmv(&(tPtr
->text
[position
+ len
]), &(tPtr
->text
[position
]), tPtr
->textLen
- position
+ 1);
415 memcpy(&(tPtr
->text
[position
]), text
, len
);
417 tPtr
->textLen
+= len
;
418 if (position
>= tPtr
->cursorPosition
) {
419 tPtr
->cursorPosition
+= len
;
426 paintTextField(tPtr
);
429 void WMDeleteTextFieldRange(WMTextField
* tPtr
, WMRange range
)
431 CHECK_CLASS(tPtr
, WC_TextField
);
433 normalizeRange(tPtr
, &range
);
438 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+ range
.count
]),
439 tPtr
->textLen
- (range
.position
+ range
.count
) + 1);
441 /* better than nothing ;) */
442 if (tPtr
->cursorPosition
> range
.position
)
443 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
], tPtr
->viewPosition
);
444 tPtr
->textLen
-= range
.count
;
445 tPtr
->cursorPosition
= range
.position
;
449 paintTextField(tPtr
);
452 char *WMGetTextFieldText(WMTextField
* tPtr
)
454 CHECK_CLASS(tPtr
, WC_TextField
);
456 return wstrdup(tPtr
->text
);
459 void WMSetTextFieldText(WMTextField
* tPtr
, const char *text
)
461 CHECK_CLASS(tPtr
, WC_TextField
);
463 if ((text
&& strcmp(tPtr
->text
, text
) == 0) || (!text
&& tPtr
->textLen
== 0))
470 tPtr
->textLen
= strlen(text
);
472 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
473 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
474 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
476 wstrlcpy(tPtr
->text
, text
, tPtr
->bufferSize
);
479 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
480 tPtr
->viewPosition
= 0;
481 tPtr
->selection
.count
= 0;
483 if (tPtr
->view
->flags
.realized
)
484 paintTextField(tPtr
);
487 void WMSetTextFieldAlignment(WMTextField
* tPtr
, WMAlignment alignment
)
489 CHECK_CLASS(tPtr
, WC_TextField
);
491 tPtr
->flags
.alignment
= alignment
;
493 if (alignment
!= WALeft
) {
494 wwarning(_("only left alignment is supported in textfields"));
498 if (tPtr
->view
->flags
.realized
) {
499 paintTextField(tPtr
);
503 void WMSetTextFieldBordered(WMTextField
* tPtr
, Bool bordered
)
505 CHECK_CLASS(tPtr
, WC_TextField
);
507 tPtr
->flags
.bordered
= bordered
;
509 if (tPtr
->view
->flags
.realized
) {
510 paintTextField(tPtr
);
514 void WMSetTextFieldBeveled(WMTextField
* tPtr
, Bool flag
)
516 CHECK_CLASS(tPtr
, WC_TextField
);
518 tPtr
->flags
.beveled
= ((flag
== 0) ? 0 : 1);
520 if (tPtr
->view
->flags
.realized
) {
521 paintTextField(tPtr
);
525 void WMSetTextFieldSecure(WMTextField
* tPtr
, Bool flag
)
527 CHECK_CLASS(tPtr
, WC_TextField
);
529 tPtr
->flags
.secure
= ((flag
== 0) ? 0 : 1);
531 if (tPtr
->view
->flags
.realized
) {
532 paintTextField(tPtr
);
536 Bool
WMGetTextFieldEditable(WMTextField
* tPtr
)
538 CHECK_CLASS(tPtr
, WC_TextField
);
540 return tPtr
->flags
.enabled
;
543 void WMSetTextFieldEditable(WMTextField
* tPtr
, Bool flag
)
545 CHECK_CLASS(tPtr
, WC_TextField
);
547 tPtr
->flags
.enabled
= ((flag
== 0) ? 0 : 1);
549 if (tPtr
->view
->flags
.realized
) {
550 paintTextField(tPtr
);
554 void WMSelectTextFieldRange(WMTextField
* tPtr
, WMRange range
)
556 CHECK_CLASS(tPtr
, WC_TextField
);
558 if (tPtr
->flags
.enabled
) {
559 normalizeRange(tPtr
, &range
);
561 tPtr
->selection
= range
;
563 tPtr
->cursorPosition
= range
.position
+ range
.count
;
565 if (tPtr
->view
->flags
.realized
) {
566 paintTextField(tPtr
);
571 void WMSetTextFieldCursorPosition(WMTextField
* tPtr
, unsigned int position
)
573 CHECK_CLASS(tPtr
, WC_TextField
);
575 if (tPtr
->flags
.enabled
) {
576 if (position
> tPtr
->textLen
)
577 position
= tPtr
->textLen
;
579 tPtr
->cursorPosition
= position
;
580 if (tPtr
->view
->flags
.realized
) {
581 paintTextField(tPtr
);
586 unsigned WMGetTextFieldCursorPosition(WMTextField
*tPtr
)
588 CHECK_CLASS(tPtr
, WC_TextField
);
590 return tPtr
->cursorPosition
;
593 void WMSetTextFieldNextTextField(WMTextField
* tPtr
, WMTextField
* next
)
595 CHECK_CLASS(tPtr
, WC_TextField
);
597 if (tPtr
->view
->nextFocusChain
)
598 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
599 tPtr
->view
->nextFocusChain
= NULL
;
603 CHECK_CLASS(next
, WC_TextField
);
605 if (tPtr
->view
->nextFocusChain
)
606 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
607 if (next
->view
->prevFocusChain
)
608 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
610 tPtr
->view
->nextFocusChain
= next
->view
;
611 next
->view
->prevFocusChain
= tPtr
->view
;
614 void WMSetTextFieldPrevTextField(WMTextField
* tPtr
, WMTextField
* prev
)
616 CHECK_CLASS(tPtr
, WC_TextField
);
618 if (tPtr
->view
->prevFocusChain
)
619 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
620 tPtr
->view
->prevFocusChain
= NULL
;
624 CHECK_CLASS(prev
, WC_TextField
);
626 if (tPtr
->view
->prevFocusChain
)
627 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
628 if (prev
->view
->nextFocusChain
)
629 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
631 tPtr
->view
->prevFocusChain
= prev
->view
;
632 prev
->view
->nextFocusChain
= tPtr
->view
;
635 void WMSetTextFieldFont(WMTextField
* tPtr
, WMFont
* font
)
637 CHECK_CLASS(tPtr
, WC_TextField
);
640 WMReleaseFont(tPtr
->font
);
641 tPtr
->font
= WMRetainFont(font
);
643 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
645 if (tPtr
->view
->flags
.realized
) {
646 paintTextField(tPtr
);
650 WMFont
*WMGetTextFieldFont(WMTextField
* tPtr
)
655 static void didResizeTextField(W_ViewDelegate
* self
, WMView
* view
)
657 WMTextField
*tPtr
= (WMTextField
*) view
->self
;
659 /* Parameter not used, but tell the compiler that it is ok */
662 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
664 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2 * tPtr
->offsetWidth
/*+ 2 */ ;
667 static char *makeHiddenString(int length
)
669 char *data
= wmalloc(length
+ 1);
671 memset(data
, '*', length
);
677 static void paintCursor(TextField
* tPtr
)
680 WMScreen
*screen
= tPtr
->view
->screen
;
684 if (tPtr
->flags
.secure
)
685 text
= makeHiddenString(strlen(tPtr
->text
));
689 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]), tPtr
->cursorPosition
- tPtr
->viewPosition
);
691 switch (tPtr
->flags
.alignment
) {
693 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
694 if (textWidth
< tPtr
->usableWidth
)
695 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
697 cx
+= tPtr
->offsetWidth
+ 1;
700 cx
+= tPtr
->offsetWidth
+ 1;
705 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
706 if (textWidth
< tPtr
->usableWidth
)
707 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- textWidth
) / 2;
709 cx
+= tPtr
->offsetWidth
;
713 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
714 cx, tPtr->offsetWidth, 1,
715 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
716 printf("%d %d\n",cx,tPtr->cursorPosition);
719 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
720 cx
, tPtr
->offsetWidth
, cx
, tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
722 W_SetPreeditPositon(tPtr
->view
, cx
, 0);
724 if (tPtr
->flags
.secure
) {
729 static void drawRelief(WMView
* view
, Bool beveled
)
731 WMScreen
*scr
= view
->screen
;
732 Display
*dpy
= scr
->display
;
736 int width
= view
->size
.width
;
737 int height
= view
->size
.height
;
739 dgc
= WMColorGC(scr
->darkGray
);
742 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
- 1, height
- 1);
746 wgc
= WMColorGC(scr
->white
);
747 lgc
= WMColorGC(scr
->gray
);
750 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
- 1, 0);
751 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
- 2, 1);
753 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
- 2);
754 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
- 3);
757 XDrawLine(dpy
, view
->window
, wgc
, 0, height
- 1, width
- 1, height
- 1);
758 XDrawLine(dpy
, view
->window
, lgc
, 1, height
- 2, width
- 2, height
- 2);
760 XDrawLine(dpy
, view
->window
, wgc
, width
- 1, 0, width
- 1, height
- 1);
761 XDrawLine(dpy
, view
->window
, lgc
, width
- 2, 1, width
- 2, height
- 3);
764 static void paintTextField(TextField
* tPtr
)
766 W_Screen
*screen
= tPtr
->view
->screen
;
767 W_View
*view
= tPtr
->view
;
777 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
780 if (!tPtr
->flags
.bordered
) {
786 if (tPtr
->flags
.secure
) {
787 text
= makeHiddenString(strlen(tPtr
->text
));
792 totalWidth
= tPtr
->view
->size
.width
- 2 * bd
;
794 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
795 view
->size
.width
, view
->size
.height
, screen
->depth
);
796 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
797 0, 0, view
->size
.width
, view
->size
.height
);
798 /* this is quite dirty */
799 viewbuffer
.screen
= view
->screen
;
800 viewbuffer
.size
= view
->size
;
801 viewbuffer
.window
= drawbuffer
;
803 if (tPtr
->textLen
> 0) {
804 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]), tPtr
->textLen
- tPtr
->viewPosition
);
806 ty
= tPtr
->offsetWidth
;
807 switch (tPtr
->flags
.alignment
) {
809 tx
= tPtr
->offsetWidth
+ 1;
810 if (tw
< tPtr
->usableWidth
)
811 XFillRectangle(screen
->display
, drawbuffer
,
812 WMColorGC(screen
->white
),
813 bd
+ tw
, bd
, totalWidth
- tw
, view
->size
.height
- 2 * bd
);
817 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
818 if (tw
< tPtr
->usableWidth
)
819 XClearArea(screen
->display
, view
->window
, bd
, bd
,
820 totalWidth
, view
->size
.height
- 2 * bd
, False
);
825 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
826 if (tw
< tPtr
->usableWidth
)
827 XClearArea(screen
->display
, view
->window
, bd
, bd
,
828 totalWidth
- tw
, view
->size
.height
- 2 * bd
, False
);
832 color
= tPtr
->flags
.enabled
? screen
->black
: screen
->darkGray
;
834 WMDrawImageString(screen
, drawbuffer
, color
, screen
->white
,
835 tPtr
->font
, tx
, ty
, &(text
[tPtr
->viewPosition
]),
836 tPtr
->textLen
- tPtr
->viewPosition
);
838 if (tPtr
->selection
.count
) {
841 count
= tPtr
->selection
.count
< 0
842 ? tPtr
->selection
.position
+ tPtr
->selection
.count
: tPtr
->selection
.position
;
843 count2
= abs(tPtr
->selection
.count
);
844 if (count
< tPtr
->viewPosition
) {
845 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
846 count
= tPtr
->viewPosition
;
849 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
, text
, count
)
850 - WMWidthOfString(tPtr
->font
, text
, tPtr
->viewPosition
);
852 WMDrawImageString(screen
, drawbuffer
, color
, screen
->gray
,
853 tPtr
->font
, rx
, ty
, &(text
[count
]), count2
);
856 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
857 bd
, bd
, totalWidth
, view
->size
.height
- 2 * bd
);
861 if (tPtr
->flags
.bordered
) {
862 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
865 if (tPtr
->flags
.secure
)
867 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
868 screen
->copyGC
, 0, 0, view
->size
.width
, view
->size
.height
, 0, 0);
869 XFreePixmap(screen
->display
, drawbuffer
);
872 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
877 static void blinkCursor(void *data
)
879 TextField
*tPtr
= (TextField
*) data
;
881 if (tPtr
->flags
.cursorOn
) {
882 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
, data
);
884 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
, data
);
887 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
890 static void handleEvents(XEvent
* event
, void *data
)
892 TextField
*tPtr
= (TextField
*) data
;
894 CHECK_CLASS(data
, WC_TextField
);
896 switch (event
->type
) {
898 W_FocusIC(tPtr
->view
);
899 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
)) != tPtr
->view
)
901 tPtr
->flags
.focused
= 1;
903 if (!tPtr
->timerID
) {
904 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
, tPtr
);
907 paintTextField(tPtr
);
909 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
911 tPtr
->flags
.notIllegalMovement
= 0;
915 W_UnFocusIC(tPtr
->view
);
916 tPtr
->flags
.focused
= 0;
919 WMDeleteTimerHandler(tPtr
->timerID
);
920 tPtr
->timerID
= NULL
;
922 paintTextField(tPtr
);
923 if (!tPtr
->flags
.notIllegalMovement
) {
924 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
925 (void *)WMIllegalTextMovement
);
930 if (event
->xexpose
.count
!= 0)
932 paintTextField(tPtr
);
936 destroyTextField(tPtr
);
941 static void handleTextFieldKeyPress(TextField
* tPtr
, XEvent
* event
)
945 char *textEvent
= NULL
;
947 int count
, refresh
= 0;
948 int control_pressed
= 0;
949 int cancelSelection
= 1;
950 Bool shifted
, controled
, modified
;
953 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
954 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
957 shifted
= (event
->xkey
.state
& ShiftMask
? True
: False
);
958 controled
= (event
->xkey
.state
& ControlMask
? True
: False
);
959 modified
= shifted
|| controled
;
961 count
= W_LookupString(tPtr
->view
, &event
->xkey
, buffer
, 63, &ksym
, NULL
);
962 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
963 buffer
[count
] = '\0';
967 #ifdef XK_ISO_Left_Tab
968 case XK_ISO_Left_Tab
:
972 if (tPtr
->view
->prevFocusChain
) {
973 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
974 tPtr
->view
->prevFocusChain
);
975 tPtr
->flags
.notIllegalMovement
= 1;
977 data
= (void *)WMBacktabTextMovement
;
979 if (tPtr
->view
->nextFocusChain
) {
980 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
981 tPtr
->view
->nextFocusChain
);
982 tPtr
->flags
.notIllegalMovement
= 1;
984 data
= (void *)WMTabTextMovement
;
986 textEvent
= WMTextDidEndEditingNotification
;
996 data
= (void *)WMEscapeTextMovement
;
997 textEvent
= WMTextDidEndEditingNotification
;
1008 data
= (void *)WMReturnTextMovement
;
1009 textEvent
= WMTextDidEndEditingNotification
;
1015 case WM_EMACSKEY_LEFT
:
1016 if (!control_pressed
)
1025 if (tPtr
->cursorPosition
> 0) {
1029 i
= tPtr
->cursorPosition
;
1030 i
+= oneUTF8CharBackward(&tPtr
->text
[i
], i
);
1032 while (i
> 0 && tPtr
->text
[i
] != ' ')
1034 while (i
> 0 && tPtr
->text
[i
] == ' ')
1037 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1039 tPtr
->cursorPosition
= i
;
1041 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1042 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1048 cancelSelection
= 0;
1054 case WM_EMACSKEY_RIGHT
:
1055 if (!control_pressed
)
1064 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1068 i
= tPtr
->cursorPosition
;
1070 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ')
1072 while (tPtr
->text
[i
] == ' ')
1075 i
+= oneUTF8CharForward(&tPtr
->text
[i
], tPtr
->textLen
- i
);
1077 tPtr
->cursorPosition
= i
;
1079 refresh
= incrToFit2(tPtr
);
1085 cancelSelection
= 0;
1091 case WM_EMACSKEY_HOME
:
1092 if (!control_pressed
)
1102 if (tPtr
->cursorPosition
> 0) {
1104 tPtr
->cursorPosition
= 0;
1105 if (tPtr
->viewPosition
> 0) {
1106 tPtr
->viewPosition
= 0;
1112 cancelSelection
= 0;
1118 case WM_EMACSKEY_END
:
1119 if (!control_pressed
)
1129 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1131 tPtr
->cursorPosition
= tPtr
->textLen
;
1132 tPtr
->viewPosition
= 0;
1134 refresh
= incrToFit(tPtr
);
1140 cancelSelection
= 0;
1146 case WM_EMACSKEY_BS
:
1147 if (!control_pressed
)
1154 if (tPtr
->selection
.count
) {
1155 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1156 data
= (void *)WMDeleteTextEvent
;
1157 textEvent
= WMTextDidChangeNotification
;
1158 } else if (tPtr
->cursorPosition
> 0) {
1159 int i
= oneUTF8CharBackward(&tPtr
->text
[tPtr
->cursorPosition
],
1160 tPtr
->cursorPosition
);
1162 range
.position
= tPtr
->cursorPosition
+ i
;
1164 WMDeleteTextFieldRange(tPtr
, range
);
1165 data
= (void *)WMDeleteTextEvent
;
1166 textEvent
= WMTextDidChangeNotification
;
1173 case WM_EMACSKEY_DEL
:
1174 if (!control_pressed
)
1184 if (tPtr
->selection
.count
) {
1185 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1186 data
= (void *)WMDeleteTextEvent
;
1187 textEvent
= WMTextDidChangeNotification
;
1188 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1190 range
.position
= tPtr
->cursorPosition
;
1191 range
.count
= oneUTF8CharForward(&tPtr
->text
[tPtr
->cursorPosition
],
1192 tPtr
->textLen
- tPtr
->cursorPosition
);
1193 WMDeleteTextFieldRange(tPtr
, range
);
1194 data
= (void *)WMDeleteTextEvent
;
1195 textEvent
= WMTextDidChangeNotification
;
1205 if (count
> 0 && !iscntrl(buffer
[0])) {
1206 if (tPtr
->selection
.count
)
1207 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1208 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1209 data
= (void *)WMInsertTextEvent
;
1210 textEvent
= WMTextDidChangeNotification
;
1219 WMRelayToNextResponder(W_VIEW(tPtr
), event
);
1223 /* Do not allow text selection in secure text fields */
1224 if (cancelSelection
|| tPtr
->flags
.secure
) {
1225 lostSelection(tPtr
->view
, XA_PRIMARY
, NULL
);
1227 if (tPtr
->selection
.count
) {
1228 tPtr
->selection
.count
= 0;
1231 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1233 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1235 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1241 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1244 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1246 if (tPtr
->delegate
) {
1247 if (textEvent
== WMTextDidBeginEditingNotification
&& tPtr
->delegate
->didBeginEditing
)
1248 (*tPtr
->delegate
->didBeginEditing
) (tPtr
->delegate
, notif
);
1250 else if (textEvent
== WMTextDidEndEditingNotification
&& tPtr
->delegate
->didEndEditing
)
1251 (*tPtr
->delegate
->didEndEditing
) (tPtr
->delegate
, notif
);
1253 else if (textEvent
== WMTextDidChangeNotification
&& tPtr
->delegate
->didChange
)
1254 (*tPtr
->delegate
->didChange
) (tPtr
->delegate
, notif
);
1257 WMPostNotification(notif
);
1258 WMReleaseNotification(notif
);
1262 paintTextField(tPtr
);
1264 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1267 static int pointToCursorPosition(TextField
* tPtr
, int x
)
1269 int a
, b
, pos
, prev
, tw
;
1271 if (tPtr
->flags
.bordered
)
1274 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1275 tPtr
->textLen
- tPtr
->viewPosition
) <= x
)
1276 return tPtr
->textLen
;
1278 a
= tPtr
->viewPosition
;
1281 /* we halve the text until we get into a 10 byte vicinity of x */
1282 while (b
- a
> 10) {
1284 pos
+= seekUTF8CharStart(&tPtr
->text
[pos
], pos
- a
);
1285 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]), pos
- tPtr
->viewPosition
);
1288 } else if (tw
< x
) {
1295 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1296 * inclusive, with the exception of the left border of the 'a' glyph and
1297 * the right border or the 'b-1' glyph
1299 * ( <--- range for x's position ---> )
1300 * a a+1 .......................... b-1 b
1304 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]), pos
- tPtr
->viewPosition
);
1307 } else if (pos
== b
) {
1311 pos
+= oneUTF8CharForward(&tPtr
->text
[pos
], b
- pos
);
1317 static void pasteText(WMView
* view
, Atom selection
, Atom target
, Time timestamp
, void *cdata
, WMData
* data
)
1319 TextField
*tPtr
= (TextField
*) view
->self
;
1322 /* Parameter not used, but tell the compiler that it is ok */
1328 tPtr
->flags
.waitingSelection
= 0;
1331 str
= (char *)WMDataBytes(data
);
1333 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1334 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, (void *)WMInsertTextEvent
);
1338 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1342 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1344 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, (void *)WMInsertTextEvent
);
1349 static void handleTextFieldActionEvents(XEvent
* event
, void *data
)
1351 TextField
*tPtr
= (TextField
*) data
;
1352 static Time lastButtonReleasedEvent
= 0;
1353 static Time lastButtonReleasedEvent2
= 0;
1354 Display
*dpy
= event
->xany
.display
;
1356 CHECK_CLASS(data
, WC_TextField
);
1358 switch (event
->type
) {
1360 if (tPtr
->flags
.waitingSelection
) {
1363 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1364 handleTextFieldKeyPress(tPtr
, event
);
1365 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->invisibleCursor
);
1366 tPtr
->flags
.pointerGrabbed
= 1;
1372 if (tPtr
->flags
.pointerGrabbed
) {
1373 tPtr
->flags
.pointerGrabbed
= 0;
1374 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1376 if (tPtr
->flags
.waitingSelection
) {
1380 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1382 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
> tPtr
->usableWidth
) {
1383 if (WMWidthOfString(tPtr
->font
,
1384 &(tPtr
->text
[tPtr
->viewPosition
]),
1385 tPtr
->cursorPosition
- tPtr
->viewPosition
)
1386 > tPtr
->usableWidth
) {
1387 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
1389 tPtr
->viewPosition
);
1391 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1393 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
],
1394 tPtr
->viewPosition
);
1397 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1399 /* Do not allow text selection in secure textfields */
1400 if (tPtr
->flags
.secure
) {
1401 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1404 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1407 paintTextField(tPtr
);
1413 if (tPtr
->flags
.pointerGrabbed
) {
1414 tPtr
->flags
.pointerGrabbed
= 0;
1415 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1419 if (tPtr
->flags
.waitingSelection
) {
1423 switch (tPtr
->flags
.alignment
) {
1426 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1427 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1428 WMSetFocusToWidget(tPtr
);
1430 if (tPtr
->flags
.focused
) {
1431 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1432 tPtr
->selection
.count
= 0;
1434 if (textWidth
< tPtr
->usableWidth
) {
1435 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1436 event
->xbutton
.x
- tPtr
->usableWidth
1439 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xbutton
.x
);
1441 paintTextField(tPtr
);
1445 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1446 WMSetFocusToWidget(tPtr
);
1448 if (tPtr
->flags
.focused
&& event
->xbutton
.button
== Button1
) {
1449 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xbutton
.x
);
1450 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1451 tPtr
->selection
.count
= 0;
1452 paintTextField(tPtr
);
1454 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1458 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1459 event
->xbutton
.time
, pasteText
, NULL
)) {
1460 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1464 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1466 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1467 (void *)WMInsertTextEvent
);
1470 tPtr
->flags
.waitingSelection
= 1;
1480 if (tPtr
->flags
.pointerGrabbed
) {
1481 tPtr
->flags
.pointerGrabbed
= 0;
1482 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1484 if (tPtr
->flags
.waitingSelection
) {
1488 if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!= 0) {
1490 XRotateBuffers(dpy
, 1);
1492 count
= abs(tPtr
->selection
.count
);
1493 if (tPtr
->selection
.count
< 0)
1494 start
= tPtr
->selection
.position
- count
;
1496 start
= tPtr
->selection
.position
;
1498 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1501 if (!tPtr
->flags
.secure
&&
1502 event
->xbutton
.time
- lastButtonReleasedEvent
<= WINGsConfiguration
.doubleClickDelay
) {
1504 if (event
->xbutton
.time
- lastButtonReleasedEvent2
<=
1505 2 * WINGsConfiguration
.doubleClickDelay
) {
1506 tPtr
->selection
.position
= 0;
1507 tPtr
->selection
.count
= tPtr
->textLen
;
1511 pos
= tPtr
->selection
.position
;
1512 cnt
= tPtr
->selection
.count
;
1515 if (txt
[pos
] == ' ' || txt
[pos
] == '\t')
1521 while (pos
+ cnt
< tPtr
->textLen
) {
1522 if (txt
[pos
+ cnt
] == ' ' || txt
[pos
+ cnt
] == '\t')
1526 tPtr
->selection
.position
= pos
;
1527 tPtr
->selection
.count
= cnt
;
1529 paintTextField(tPtr
);
1531 if (!tPtr
->flags
.ownsSelection
) {
1532 tPtr
->flags
.ownsSelection
=
1533 WMCreateSelectionHandler(tPtr
->view
,
1535 event
->xbutton
.time
, &selectionHandler
, NULL
);
1537 } else if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!= 0 && !tPtr
->flags
.ownsSelection
) {
1538 tPtr
->flags
.ownsSelection
=
1539 WMCreateSelectionHandler(tPtr
->view
,
1540 XA_PRIMARY
, event
->xbutton
.time
, &selectionHandler
, NULL
);
1543 lastButtonReleasedEvent2
= lastButtonReleasedEvent
;
1544 lastButtonReleasedEvent
= event
->xbutton
.time
;
1550 static void destroyTextField(TextField
* tPtr
)
1553 WMDeleteTimerHandler(tPtr
->timerID
);
1555 W_DestroyIC(tPtr
->view
);
1557 WMReleaseFont(tPtr
->font
);
1558 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1559 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1560 WMRemoveNotificationObserver(tPtr
);