7 #include <X11/keysym.h>
12 #define CURSOR_BLINK_ON_DELAY 600
13 #define CURSOR_BLINK_OFF_DELAY 300
17 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
18 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
19 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
22 typedef struct W_TextField
{
27 struct W_TextField
*nextField
; /* next textfield in the chain */
28 struct W_TextField
*prevField
;
32 int textLen
; /* size of text */
33 int bufferSize
; /* memory allocated for text */
35 int viewPosition
; /* position of text being shown */
37 int cursorPosition
; /* position of the insertion cursor */
40 short offsetWidth
; /* offset of text from border */
43 WMRange prevselection
;
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;
70 unsigned int notIllegalMovement
:1;
75 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
76 if ((T)->delegate && (T)->delegate->C)\
77 (*(T)->delegate->C)((T)->delegate,notif);\
78 WMPostNotification(notif);\
79 WMReleaseNotification(notif);}
82 #define MIN_TEXT_BUFFER 2
83 #define TEXT_BUFFER_INCR 8
86 #define WM_EMACSKEYMASK ControlMask
88 #define WM_EMACSKEY_LEFT XK_b
89 #define WM_EMACSKEY_RIGHT XK_f
90 #define WM_EMACSKEY_HOME XK_a
91 #define WM_EMACSKEY_END XK_e
92 #define WM_EMACSKEY_BS XK_h
93 #define WM_EMACSKEY_DEL XK_d
97 #define DEFAULT_WIDTH 60
98 #define DEFAULT_HEIGHT 20
99 #define DEFAULT_BORDERED True
100 #define DEFAULT_ALIGNMENT WALeft
104 static void destroyTextField(TextField
*tPtr
);
105 static void paintTextField(TextField
*tPtr
);
107 static void handleEvents(XEvent
*event
, void *data
);
108 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
109 static void didResizeTextField();
111 struct W_ViewDelegate _TextFieldViewDelegate
= {
120 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
121 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
123 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
124 &((tPtr)->text[(start)]), (end) - (start) + 1))
128 memmv(char *dest
, char *src
, int size
)
133 for (i
=size
-1; i
>=0; i
--) {
136 } else if (dest
< src
) {
137 for (i
=0; i
<size
; i
++) {
145 incrToFit(TextField
*tPtr
)
147 int vp
= tPtr
->viewPosition
;
149 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
150 tPtr
->viewPosition
++;
152 return vp
!=tPtr
->viewPosition
;
157 incrToFit2(TextField
*tPtr
)
159 int vp
= tPtr
->viewPosition
;
160 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
161 >= tPtr
->usableWidth
)
162 tPtr
->viewPosition
++;
165 return vp
!=tPtr
->viewPosition
;
170 decrToFit(TextField
*tPtr
)
172 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
173 && tPtr
->viewPosition
>0)
174 tPtr
->viewPosition
--;
181 requestHandler(WMWidget
*w
, Atom selection
, Atom target
, Atom
*type
,
182 void **value
, unsigned *length
, int *format
)
186 Display
*dpy
= tPtr
->view
->screen
->display
;
189 text
= XGetAtomName(tPtr
->view
->screen
->display
,target
);
191 text
= XGetAtomName(tPtr
->view
->screen
->display
,selection
);
197 count
= tPtr
->selection
.count
< 0
198 ? tPtr
->selection
.position
+ tPtr
->selection
.count
199 : tPtr
->selection
.position
;
201 if (target
== XA_STRING
||
202 target
== XInternAtom(dpy
, "TEXT", False
) ||
203 target
== XInternAtom(dpy
, "COMPOUND_TEXT", False
)) {
204 *value
= wstrdup(&(tPtr
->text
[count
]));
205 *length
= abs(tPtr
->selection
.count
);
211 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
212 if (target
== _TARGETS
) {
216 ptr
= *value
= (char *) wmalloc(4 * sizeof(Atom
));
219 ptr
[2] = XInternAtom(dpy
, "TEXT", False
);
220 ptr
[3] = XInternAtom(dpy
, "COMPOUND_TEXT", False
);
225 *target = XA_PRIMARY;
233 lostHandler(WMWidget
*w
, Atom selection
)
235 TextField
*tPtr
= (WMTextField
*)w
;
237 tPtr
->selection
.count
= 0;
238 paintTextField(tPtr
);
242 _notification(void *observerData
, WMNotification
*notification
)
244 WMTextField
*to
= (WMTextField
*)observerData
;
245 WMTextField
*tw
= (WMTextField
*)WMGetNotificationClientData(notification
);
246 if (to
!= tw
) lostHandler(to
, 0);
250 WMCreateTextField(WMWidget
*parent
)
254 tPtr
= wmalloc(sizeof(TextField
));
255 memset(tPtr
, 0, sizeof(TextField
));
257 tPtr
->widgetClass
= WC_TextField
;
259 tPtr
->view
= W_CreateView(W_VIEW(parent
));
264 tPtr
->view
->self
= tPtr
;
266 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
268 tPtr
->view
->attribFlags
|= CWCursor
;
269 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
271 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
273 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
276 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
278 tPtr
->flags
.enabled
= 1;
280 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
281 |FocusChangeMask
, handleEvents
, tPtr
);
283 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
285 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
286 tPtr
->flags
.beveled
= True
;
287 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
289 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
291 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
293 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
294 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
295 handleTextFieldActionEvents
, tPtr
);
297 WMCreateSelectionHandler(tPtr
, XA_PRIMARY
, CurrentTime
, requestHandler
,
299 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
302 tPtr
->flags
.cursorOn
= 1;
309 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
311 tPtr
->delegate
= delegate
;
316 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
320 CHECK_CLASS(tPtr
, WC_TextField
);
327 /* check if buffer will hold the text */
328 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
329 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
330 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
333 if (position
< 0 || position
>= tPtr
->textLen
) {
334 /* append the text at the end */
335 strcat(tPtr
->text
, text
);
339 tPtr
->textLen
+= len
;
340 tPtr
->cursorPosition
+= len
;
342 /* insert text at position */
343 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
344 tPtr
->textLen
-position
+1);
346 memcpy(&(tPtr
->text
[position
]), text
, len
);
348 tPtr
->textLen
+= len
;
349 if (position
>= tPtr
->cursorPosition
) {
350 tPtr
->cursorPosition
+= len
;
357 paintTextField(tPtr
);
362 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
364 CHECK_CLASS(tPtr
, WC_TextField
);
366 if (range
.position
>= tPtr
->textLen
)
369 if (range
.count
< 1) {
370 if (range
.position
< 0)
372 tPtr
->text
[range
.position
] = 0;
373 tPtr
->textLen
= range
.position
;
375 tPtr
->cursorPosition
= 0;
376 tPtr
->viewPosition
= 0;
378 if (range
.position
+ range
.count
> tPtr
->textLen
)
379 range
.count
= tPtr
->textLen
- range
.position
;
380 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
381 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
382 tPtr
->textLen
-= range
.count
;
384 if (tPtr
->cursorPosition
> range
.position
)
385 tPtr
->cursorPosition
-= range
.count
;
390 paintTextField(tPtr
);
396 WMGetTextFieldText(WMTextField
*tPtr
)
398 CHECK_CLASS(tPtr
, WC_TextField
);
400 return wstrdup(tPtr
->text
);
405 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
407 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
408 (!text
&& tPtr
->textLen
== 0))
415 tPtr
->textLen
= strlen(text
);
417 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
418 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
419 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
421 strcpy(tPtr
->text
, text
);
424 if (tPtr->textLen < tPtr->cursorPosition)
425 tPtr->cursorPosition = tPtr->textLen;
427 tPtr
->cursorPosition
= tPtr
->textLen
;
428 tPtr
->viewPosition
= 0;
429 tPtr
->selection
.count
= 0;
431 if (tPtr
->view
->flags
.realized
)
432 paintTextField(tPtr
);
437 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
439 tPtr
->flags
.alignment
= alignment
;
440 if (alignment
!=WALeft
) {
441 wwarning("only left alignment is supported in textfields");
445 if (tPtr
->view
->flags
.realized
) {
446 paintTextField(tPtr
);
452 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
454 tPtr
->flags
.bordered
= bordered
;
456 if (tPtr
->view
->flags
.realized
) {
457 paintTextField(tPtr
);
463 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
465 tPtr
->flags
.beveled
= flag
;
467 if (tPtr
->view
->flags
.realized
) {
468 paintTextField(tPtr
);
475 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
477 tPtr
->flags
.secure
= flag
;
479 if (tPtr
->view
->flags
.realized
) {
480 paintTextField(tPtr
);
486 WMGetTextFieldEditable(WMTextField
*tPtr
)
488 return tPtr
->flags
.enabled
;
493 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
495 tPtr
->flags
.enabled
= flag
;
497 if (tPtr
->view
->flags
.realized
) {
498 paintTextField(tPtr
);
504 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
506 if (tPtr
->flags
.enabled
) {
507 if (range
.position
< 0) {
508 range
.count
+= range
.position
;
509 range
.count
= (range
.count
< 0) ? 0 : range
.count
;
511 } else if (range
.position
> tPtr
->textLen
) {
512 range
.position
= tPtr
->textLen
;
516 if (range
.position
+ range
.count
> tPtr
->textLen
)
517 range
.count
= tPtr
->textLen
- range
.position
;
519 tPtr
->prevselection
= tPtr
->selection
; /* check if this is needed */
521 tPtr
->selection
= range
;
523 if (tPtr
->view
->flags
.realized
) {
524 paintTextField(tPtr
);
531 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
533 if (tPtr
->flags
.enabled
) {
534 if (position
> tPtr
->textLen
)
535 position
= tPtr
->textLen
;
537 tPtr
->cursorPosition
= position
;
538 if (tPtr
->view
->flags
.realized
) {
539 paintTextField(tPtr
);
546 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
548 CHECK_CLASS(tPtr
, WC_TextField
);
550 if (tPtr
->view
->nextFocusChain
)
551 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
552 tPtr
->view
->nextFocusChain
= NULL
;
556 CHECK_CLASS(next
, WC_TextField
);
558 if (tPtr
->view
->nextFocusChain
)
559 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
560 if (next
->view
->prevFocusChain
)
561 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
563 tPtr
->view
->nextFocusChain
= next
->view
;
564 next
->view
->prevFocusChain
= tPtr
->view
;
569 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
571 CHECK_CLASS(tPtr
, WC_TextField
);
573 if (tPtr
->view
->prevFocusChain
)
574 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
575 tPtr
->view
->prevFocusChain
= NULL
;
579 CHECK_CLASS(prev
, WC_TextField
);
581 if (tPtr
->view
->prevFocusChain
)
582 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
583 if (prev
->view
->nextFocusChain
)
584 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
586 tPtr
->view
->prevFocusChain
= prev
->view
;
587 prev
->view
->nextFocusChain
= tPtr
->view
;
592 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
595 WMReleaseFont(tPtr
->font
);
596 tPtr
->font
= WMRetainFont(font
);
599 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
601 if (tPtr
->view
->flags
.realized
) {
602 paintTextField(tPtr
);
608 WMGetTextFieldFont(WMTextField
*tPtr
)
616 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
618 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
621 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
623 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
628 makeHiddenString(int length
)
630 char *data
= wmalloc(length
+1);
632 memset(data
, '*', length
);
640 paintCursor(TextField
*tPtr
)
643 WMScreen
*screen
= tPtr
->view
->screen
;
647 if (tPtr
->flags
.secure
)
648 text
= makeHiddenString(strlen(tPtr
->text
));
652 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
653 tPtr
->cursorPosition
-tPtr
->viewPosition
);
655 switch (tPtr
->flags
.alignment
) {
657 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
658 if (textWidth
< tPtr
->usableWidth
)
659 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
661 cx
+= tPtr
->offsetWidth
+ 1;
664 cx
+= tPtr
->offsetWidth
+ 1;
669 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
670 if (textWidth
< tPtr
->usableWidth
)
671 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
673 cx
+= tPtr
->offsetWidth
;
677 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
678 cx, tPtr->offsetWidth, 1,
679 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
680 printf("%d %d\n",cx,tPtr->cursorPosition);
682 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
683 cx
, tPtr
->offsetWidth
, cx
,
684 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
686 if (tPtr
->flags
.secure
)
693 drawRelief(WMView
*view
, Bool beveled
)
695 WMScreen
*scr
= view
->screen
;
696 Display
*dpy
= scr
->display
;
700 int width
= view
->size
.width
;
701 int height
= view
->size
.height
;
703 dgc
= WMColorGC(scr
->darkGray
);
706 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
710 wgc
= WMColorGC(scr
->white
);
711 lgc
= WMColorGC(scr
->gray
);
714 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
715 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
717 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
718 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
721 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
722 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
724 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
725 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
730 paintTextField(TextField
*tPtr
)
732 W_Screen
*screen
= tPtr
->view
->screen
;
733 W_View
*view
= tPtr
->view
;
743 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
746 if (!tPtr
->flags
.bordered
) {
752 if (tPtr
->flags
.secure
) {
753 text
= makeHiddenString(strlen(tPtr
->text
));
758 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
760 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
761 view
->size
.width
, view
->size
.height
, screen
->depth
);
762 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
763 0,0, view
->size
.width
,view
->size
.height
);
764 /* this is quite dirty */
765 viewbuffer
.screen
= view
->screen
;
766 viewbuffer
.size
= view
->size
;
767 viewbuffer
.window
= drawbuffer
;
770 if (tPtr
->textLen
> 0) {
771 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
772 tPtr
->textLen
- tPtr
->viewPosition
);
774 th
= WMFontHeight(tPtr
->font
);
776 ty
= tPtr
->offsetWidth
;
777 switch (tPtr
->flags
.alignment
) {
779 tx
= tPtr
->offsetWidth
+ 1;
780 if (tw
< tPtr
->usableWidth
)
781 XFillRectangle(screen
->display
, drawbuffer
,
782 WMColorGC(screen
->white
),
783 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
787 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
788 if (tw
< tPtr
->usableWidth
)
789 XClearArea(screen
->display
, view
->window
, bd
, bd
,
790 totalWidth
, view
->size
.height
-2*bd
, False
);
795 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
796 if (tw
< tPtr
->usableWidth
)
797 XClearArea(screen
->display
, view
->window
, bd
, bd
,
798 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
802 if (!tPtr
->flags
.enabled
)
803 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
805 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
807 &(text
[tPtr
->viewPosition
]),
808 tPtr
->textLen
- tPtr
->viewPosition
);
810 if (tPtr
->selection
.count
) {
813 count
= tPtr
->selection
.count
< 0
814 ? tPtr
->selection
.position
+ tPtr
->selection
.count
815 : tPtr
->selection
.position
;
816 count2
= abs(tPtr
->selection
.count
);
817 if (count
< tPtr
->viewPosition
) {
818 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
819 count
= tPtr
->viewPosition
;
823 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
824 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
826 XSetBackground(screen
->display
, screen
->textFieldGC
,
827 screen
->gray
->color
.pixel
);
829 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
830 tPtr
->font
, rx
, ty
, &(text
[count
]),
833 XSetBackground(screen
->display
, screen
->textFieldGC
,
834 screen
->white
->color
.pixel
);
837 if (!tPtr
->flags
.enabled
)
838 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
840 XFillRectangle(screen
->display
, drawbuffer
,
841 WMColorGC(screen
->white
),
842 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
846 if (tPtr
->flags
.bordered
) {
847 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
850 if (tPtr
->flags
.secure
)
852 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
853 screen
->copyGC
, 0,0, view
->size
.width
,
854 view
->size
.height
,0,0);
855 XFreePixmap(screen
->display
, drawbuffer
);
858 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
866 blinkCursor(void *data
)
868 TextField
*tPtr
= (TextField
*)data
;
870 if (tPtr
->flags
.cursorOn
) {
871 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
874 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
878 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
884 handleEvents(XEvent
*event
, void *data
)
886 TextField
*tPtr
= (TextField
*)data
;
888 CHECK_CLASS(data
, WC_TextField
);
891 switch (event
->type
) {
893 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
895 tPtr
->flags
.focused
= 1;
897 if (!tPtr
->timerID
) {
898 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
902 paintTextField(tPtr
);
904 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
906 tPtr
->flags
.notIllegalMovement
= 0;
910 tPtr
->flags
.focused
= 0;
913 WMDeleteTimerHandler(tPtr
->timerID
);
914 tPtr
->timerID
= NULL
;
917 paintTextField(tPtr
);
918 if (!tPtr
->flags
.notIllegalMovement
) {
919 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
920 (void*)WMIllegalTextMovement
);
925 if (event
->xexpose
.count
!=0)
927 paintTextField(tPtr
);
931 destroyTextField(tPtr
);
938 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
942 int count
, refresh
= 0;
943 int control_pressed
= 0;
945 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
) {
949 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
950 buffer
[count
] = '\0';
952 if (!(event
->xkey
.state
& ShiftMask
)) {
953 if (tPtr
->selection
.count
)
955 tPtr
->prevselection
= tPtr
->selection
;
956 tPtr
->selection
.position
= tPtr
->cursorPosition
;
957 tPtr
->selection
.count
= 0;
960 /* Be careful in any case in this switch statement, never to call
961 * to more than a function that can generate text change notifications.
962 * Only one text change notification should be sent in any case.
963 * Else hazardous things can happen.
964 * Maybe we need a better solution than the function wrapper to inform
965 * functions that change text in text fields, if they need to send a
966 * change notification or not. -Dan
970 #ifdef XK_ISO_Left_Tab
971 case XK_ISO_Left_Tab
:
973 if (event
->xkey
.state
& ShiftMask
) {
974 if (tPtr
->view
->prevFocusChain
) {
975 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
976 tPtr
->view
->prevFocusChain
);
977 tPtr
->flags
.notIllegalMovement
= 1;
979 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
980 (void*)WMBacktabTextMovement
);
982 if (tPtr
->view
->nextFocusChain
) {
983 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
984 tPtr
->view
->nextFocusChain
);
985 tPtr
->flags
.notIllegalMovement
= 1;
987 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
988 (void*)WMTabTextMovement
);
993 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
994 (void*)WMReturnTextMovement
);
997 case WM_EMACSKEY_LEFT
:
998 if (!control_pressed
) {
1005 if (tPtr
->cursorPosition
> 0) {
1007 if (event
->xkey
.state
& ControlMask
) {
1009 for (i
= tPtr
->cursorPosition
- 1; i
>= 0; i
--)
1010 if (tPtr
->text
[i
] == ' ' || i
== 0) {
1011 tPtr
->cursorPosition
= i
;
1015 tPtr
->cursorPosition
--;
1017 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1018 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1026 case WM_EMACSKEY_RIGHT
:
1027 if (!control_pressed
) {
1034 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1036 if (event
->xkey
.state
& ControlMask
) {
1038 for (i
= tPtr
->cursorPosition
+ 1; i
<= tPtr
->textLen
; i
++)
1039 if (tPtr
->text
[i
] == ' ' || i
== tPtr
->textLen
) {
1040 tPtr
->cursorPosition
= i
;
1044 tPtr
->cursorPosition
++;
1046 while (WMWidthOfString(tPtr
->font
,
1047 &(tPtr
->text
[tPtr
->viewPosition
]),
1048 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1049 > tPtr
->usableWidth
) {
1050 tPtr
->viewPosition
++;
1058 case WM_EMACSKEY_HOME
:
1059 if (!control_pressed
) {
1066 if (tPtr
->cursorPosition
> 0) {
1068 tPtr
->cursorPosition
= 0;
1069 if (tPtr
->viewPosition
> 0) {
1070 tPtr
->viewPosition
= 0;
1078 case WM_EMACSKEY_END
:
1079 if (!control_pressed
) {
1086 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1088 tPtr
->cursorPosition
= tPtr
->textLen
;
1089 tPtr
->viewPosition
= 0;
1090 while (WMWidthOfString(tPtr
->font
,
1091 &(tPtr
->text
[tPtr
->viewPosition
]),
1092 tPtr
->textLen
-tPtr
->viewPosition
)
1093 > tPtr
->usableWidth
) {
1094 tPtr
->viewPosition
++;
1102 case WM_EMACSKEY_BS
:
1103 if (!control_pressed
) {
1107 if (tPtr
->cursorPosition
> 0) {
1110 if (tPtr
->prevselection
.count
) {
1111 range
.position
= tPtr
->prevselection
.count
< 0
1112 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1113 : tPtr
->prevselection
.position
;
1115 range
.count
= abs(tPtr
->prevselection
.count
);
1117 range
.position
= tPtr
->cursorPosition
- 1;
1120 WMDeleteTextFieldRange(tPtr
, range
);
1121 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1122 (void*)WMDeleteTextEvent
);
1126 case WM_EMACSKEY_DEL
:
1127 if (!control_pressed
) {
1134 if (tPtr
->cursorPosition
< tPtr
->textLen
|| tPtr
->prevselection
.count
) {
1137 if (tPtr
->prevselection
.count
) {
1138 range
.position
= tPtr
->prevselection
.count
< 0
1139 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1140 : tPtr
->prevselection
.position
;
1142 range
.count
= abs(tPtr
->prevselection
.count
);
1144 range
.position
= tPtr
->cursorPosition
;
1147 WMDeleteTextFieldRange(tPtr
, range
);
1148 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1149 (void*)WMDeleteTextEvent
);
1155 if (count
> 0 && !iscntrl(buffer
[0])) {
1158 if (tPtr
->prevselection
.count
) {
1159 range
.position
= tPtr
->prevselection
.count
< 0
1160 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1161 : tPtr
->prevselection
.position
;
1163 range
.count
= abs(tPtr
->prevselection
.count
);
1165 range
.position
= tPtr
->cursorPosition
;
1168 if (tPtr
->prevselection
.count
)
1169 WMDeleteTextFieldRange(tPtr
, range
);
1170 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1171 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1172 (void*)WMInsertTextEvent
);
1178 if (event
->xkey
.state
& ShiftMask
) {
1179 if (tPtr
->selection
.count
== 0)
1180 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1181 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1184 tPtr
->prevselection
.count
= 0;
1186 paintTextField(tPtr
);
1192 pointToCursorPosition(TextField
*tPtr
, int x
)
1197 if (tPtr
->flags
.bordered
)
1200 a
= tPtr
->viewPosition
;
1201 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1202 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1203 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1204 return tPtr
->textLen
;
1206 while (a
< b
&& b
-a
>1) {
1208 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1209 mid
- tPtr
->viewPosition
);
1222 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1224 TextField
*tPtr
= (TextField
*)data
;
1227 CHECK_CLASS(data
, WC_TextField
);
1229 switch (event
->type
) {
1231 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1232 handleTextFieldKeyPress(tPtr
, event
);
1233 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1234 W_VIEW(tPtr
)->window
, False
,
1235 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1236 GrabModeAsync
, GrabModeAsync
, None
,
1237 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1239 tPtr
->flags
.pointerGrabbed
= 1;
1245 if (tPtr
->flags
.pointerGrabbed
) {
1246 tPtr
->flags
.pointerGrabbed
= 0;
1247 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1250 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1252 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1253 tPtr
->usableWidth
) {
1254 if (WMWidthOfString(tPtr
->font
,
1255 &(tPtr
->text
[tPtr
->viewPosition
]),
1256 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1257 > tPtr
->usableWidth
) {
1258 tPtr
->viewPosition
++;
1260 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1262 tPtr
->viewPosition
--;
1265 if (!tPtr
->selection
.count
) {
1266 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1269 tPtr
->cursorPosition
=
1270 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1272 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1275 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1279 paintTextField(tPtr
);
1283 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1284 XA_PRIMARY
, tPtr
->view
->window
, event
->xmotion
.time
);
1286 WMNotification
*notif
= WMCreateNotification("_lostOwnership",
1288 WMPostNotification(notif
);
1289 WMReleaseNotification(notif
);
1295 if (tPtr
->flags
.pointerGrabbed
) {
1296 tPtr
->flags
.pointerGrabbed
= 0;
1297 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1302 switch (tPtr
->flags
.alignment
) {
1305 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1306 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1307 WMSetFocusToWidget(tPtr
);
1308 } else if (tPtr
->flags
.focused
) {
1309 tPtr
->selection
.count
= 0;
1311 if(textWidth
< tPtr
->usableWidth
){
1312 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1313 event
->xbutton
.x
- tPtr
->usableWidth
1316 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1319 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1321 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1324 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1327 paintTextField(tPtr
);
1331 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1332 WMSetFocusToWidget(tPtr
);
1333 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1335 paintTextField(tPtr
);
1336 } else if (tPtr
->flags
.focused
) {
1337 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1339 tPtr
->selection
.count
= 0;
1340 paintTextField(tPtr
);
1342 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1345 text
= W_GetTextSelection(tPtr
->view
->screen
, XA_PRIMARY
);
1348 text
= W_GetTextSelection(tPtr
->view
->screen
,
1349 tPtr
->view
->screen
->clipboardAtom
);
1352 text
= W_GetTextSelection(tPtr
->view
->screen
,
1356 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1358 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1359 (void*)WMInsertTextEvent
);
1367 if (tPtr
->flags
.pointerGrabbed
) {
1368 tPtr
->flags
.pointerGrabbed
= 0;
1369 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1379 destroyTextField(TextField
*tPtr
)
1383 WMDeleteTimerHandler(tPtr
->timerID
);
1386 WMReleaseFont(tPtr
->font
);
1387 WMDeleteSelectionHandler(tPtr
, XA_PRIMARY
);
1388 WMRemoveNotificationObserver(tPtr
);