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 resizeTextField();
111 struct W_ViewProcedureTable _TextFieldViewProcedures
= {
118 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
119 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
121 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
122 &((tPtr)->text[(start)]), (end) - (start) + 1))
126 memmv(char *dest
, char *src
, int size
)
131 for (i
=size
-1; i
>=0; i
--) {
134 } else if (dest
< src
) {
135 for (i
=0; i
<size
; i
++) {
143 incrToFit(TextField
*tPtr
)
145 int vp
= tPtr
->viewPosition
;
147 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
148 tPtr
->viewPosition
++;
150 return vp
!=tPtr
->viewPosition
;
155 incrToFit2(TextField
*tPtr
)
157 int vp
= tPtr
->viewPosition
;
158 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
159 >= tPtr
->usableWidth
)
160 tPtr
->viewPosition
++;
163 return vp
!=tPtr
->viewPosition
;
168 decrToFit(TextField
*tPtr
)
170 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
171 && tPtr
->viewPosition
>0)
172 tPtr
->viewPosition
--;
179 requestHandler(WMWidget
*w
, Atom selection
, Atom target
, Atom
*type
,
180 void **value
, unsigned *length
, int *format
)
184 Display
*dpy
= tPtr
->view
->screen
->display
;
187 text
= XGetAtomName(tPtr
->view
->screen
->display
,target
);
189 text
= XGetAtomName(tPtr
->view
->screen
->display
,selection
);
195 count
= tPtr
->selection
.count
< 0
196 ? tPtr
->selection
.position
+ tPtr
->selection
.count
197 : tPtr
->selection
.position
;
199 if (target
== XA_STRING
||
200 target
== XInternAtom(dpy
, "TEXT", False
) ||
201 target
== XInternAtom(dpy
, "COMPOUND_TEXT", False
)) {
202 *value
= wstrdup(&(tPtr
->text
[count
]));
203 *length
= abs(tPtr
->selection
.count
);
209 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
210 if (target
== _TARGETS
) {
214 ptr
= *value
= (char *) wmalloc(4 * sizeof(Atom
));
217 ptr
[2] = XInternAtom(dpy
, "TEXT", False
);
218 ptr
[3] = XInternAtom(dpy
, "COMPOUND_TEXT", False
);
223 *target = XA_PRIMARY;
231 lostHandler(WMWidget
*w
, Atom selection
)
233 TextField
*tPtr
= (WMTextField
*)w
;
235 tPtr
->selection
.count
= 0;
236 paintTextField(tPtr
);
240 _notification(void *observerData
, WMNotification
*notification
)
242 WMTextField
*to
= (WMTextField
*)observerData
;
243 WMTextField
*tw
= (WMTextField
*)WMGetNotificationClientData(notification
);
244 if (to
!= tw
) lostHandler(to
, 0);
248 WMCreateTextField(WMWidget
*parent
)
252 tPtr
= wmalloc(sizeof(TextField
));
253 memset(tPtr
, 0, sizeof(TextField
));
255 tPtr
->widgetClass
= WC_TextField
;
257 tPtr
->view
= W_CreateView(W_VIEW(parent
));
262 tPtr
->view
->self
= tPtr
;
264 tPtr
->view
->attribFlags
|= CWCursor
;
265 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
267 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
269 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
272 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
274 tPtr
->flags
.enabled
= 1;
276 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
277 |FocusChangeMask
, handleEvents
, tPtr
);
279 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
281 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
283 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
284 tPtr
->flags
.beveled
= True
;
285 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
287 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
289 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
290 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
291 handleTextFieldActionEvents
, tPtr
);
293 WMCreateSelectionHandler(tPtr
, XA_PRIMARY
, CurrentTime
, requestHandler
,
295 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
298 tPtr
->flags
.cursorOn
= 1;
305 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
307 tPtr
->delegate
= delegate
;
312 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
316 CHECK_CLASS(tPtr
, WC_TextField
);
323 /* check if buffer will hold the text */
324 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
325 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
326 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
329 if (position
< 0 || position
>= tPtr
->textLen
) {
330 /* append the text at the end */
331 strcat(tPtr
->text
, text
);
335 tPtr
->textLen
+= len
;
336 tPtr
->cursorPosition
+= len
;
338 /* insert text at position */
339 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
340 tPtr
->textLen
-position
+1);
342 memcpy(&(tPtr
->text
[position
]), text
, len
);
344 tPtr
->textLen
+= len
;
345 if (position
>= tPtr
->cursorPosition
) {
346 tPtr
->cursorPosition
+= len
;
353 paintTextField(tPtr
);
358 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
360 CHECK_CLASS(tPtr
, WC_TextField
);
362 if (range
.position
>= tPtr
->textLen
)
365 if (range
.count
< 1) {
366 if (range
.position
< 0)
368 tPtr
->text
[range
.position
] = 0;
369 tPtr
->textLen
= range
.position
;
371 tPtr
->cursorPosition
= 0;
372 tPtr
->viewPosition
= 0;
374 if (range
.position
+ range
.count
> tPtr
->textLen
)
375 range
.count
= tPtr
->textLen
- range
.position
;
376 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
377 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
378 tPtr
->textLen
-= range
.count
;
380 if (tPtr
->cursorPosition
> range
.position
)
381 tPtr
->cursorPosition
-= range
.count
;
386 paintTextField(tPtr
);
392 WMGetTextFieldText(WMTextField
*tPtr
)
394 CHECK_CLASS(tPtr
, WC_TextField
);
396 return wstrdup(tPtr
->text
);
401 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
403 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
404 (!text
&& tPtr
->textLen
== 0))
411 tPtr
->textLen
= strlen(text
);
413 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
414 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
415 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
417 strcpy(tPtr
->text
, text
);
420 if (tPtr->textLen < tPtr->cursorPosition)
421 tPtr->cursorPosition = tPtr->textLen;
423 tPtr
->cursorPosition
= tPtr
->textLen
;
424 tPtr
->viewPosition
= 0;
425 tPtr
->selection
.count
= 0;
427 if (tPtr
->view
->flags
.realized
)
428 paintTextField(tPtr
);
433 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
435 /* TODO: update font change after field is mapped */
436 WMReleaseFont(tPtr
->font
);
437 tPtr
->font
= WMRetainFont(font
);
442 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
444 tPtr
->flags
.alignment
= alignment
;
445 if (alignment
!=WALeft
) {
446 wwarning("only left alignment is supported in textfields");
450 if (tPtr
->view
->flags
.realized
) {
451 paintTextField(tPtr
);
457 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
459 tPtr
->flags
.bordered
= bordered
;
461 if (tPtr
->view
->flags
.realized
) {
462 paintTextField(tPtr
);
468 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
470 tPtr
->flags
.beveled
= flag
;
472 if (tPtr
->view
->flags
.realized
) {
473 paintTextField(tPtr
);
480 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
482 tPtr
->flags
.secure
= flag
;
484 if (tPtr
->view
->flags
.realized
) {
485 paintTextField(tPtr
);
491 WMGetTextFieldEditable(WMTextField
*tPtr
)
493 return tPtr
->flags
.enabled
;
498 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
500 tPtr
->flags
.enabled
= flag
;
502 if (tPtr
->view
->flags
.realized
) {
503 paintTextField(tPtr
);
509 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
511 if (tPtr
->flags
.enabled
) {
512 if (range
.position
< 0) {
513 range
.count
+= range
.position
;
514 range
.count
= (range
.count
< 0) ? 0 : range
.count
;
516 } else if (range
.position
> tPtr
->textLen
) {
517 range
.position
= tPtr
->textLen
;
521 if (range
.position
+ range
.count
> tPtr
->textLen
)
522 range
.count
= tPtr
->textLen
- range
.position
;
524 tPtr
->prevselection
= tPtr
->selection
; /* check if this is needed */
526 tPtr
->selection
= range
;
528 if (tPtr
->view
->flags
.realized
) {
529 paintTextField(tPtr
);
536 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
538 if (tPtr
->flags
.enabled
) {
539 if (position
> tPtr
->textLen
)
540 position
= tPtr
->textLen
;
542 tPtr
->cursorPosition
= position
;
543 if (tPtr
->view
->flags
.realized
) {
544 paintTextField(tPtr
);
551 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
553 CHECK_CLASS(tPtr
, WC_TextField
);
555 if (tPtr
->view
->nextFocusChain
)
556 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
557 tPtr
->view
->nextFocusChain
= NULL
;
561 CHECK_CLASS(next
, WC_TextField
);
563 if (tPtr
->view
->nextFocusChain
)
564 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
565 if (next
->view
->prevFocusChain
)
566 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
568 tPtr
->view
->nextFocusChain
= next
->view
;
569 next
->view
->prevFocusChain
= tPtr
->view
;
574 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
576 CHECK_CLASS(tPtr
, WC_TextField
);
578 if (tPtr
->view
->prevFocusChain
)
579 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
580 tPtr
->view
->prevFocusChain
= NULL
;
584 CHECK_CLASS(prev
, WC_TextField
);
586 if (tPtr
->view
->prevFocusChain
)
587 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
588 if (prev
->view
->nextFocusChain
)
589 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
591 tPtr
->view
->prevFocusChain
= prev
->view
;
592 prev
->view
->nextFocusChain
= tPtr
->view
;
597 resizeTextField(WMTextField
*tPtr
, unsigned int width
, unsigned int height
)
599 W_ResizeView(tPtr
->view
, width
, height
);
602 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
604 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
609 makeHiddenString(int length
)
611 char *data
= wmalloc(length
+1);
613 memset(data
, '*', length
);
620 paintCursor(TextField
*tPtr
)
623 WMScreen
*screen
= tPtr
->view
->screen
;
627 if (tPtr
->flags
.secure
)
628 text
= makeHiddenString(strlen(tPtr
->text
));
632 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
633 tPtr
->cursorPosition
-tPtr
->viewPosition
);
635 switch (tPtr
->flags
.alignment
) {
637 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
638 if (textWidth
< tPtr
->usableWidth
)
639 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
641 cx
+= tPtr
->offsetWidth
+ 1;
644 cx
+= tPtr
->offsetWidth
+ 1;
649 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
650 if (textWidth
< tPtr
->usableWidth
)
651 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
653 cx
+= tPtr
->offsetWidth
;
657 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
658 cx, tPtr->offsetWidth, 1,
659 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
660 printf("%d %d\n",cx,tPtr->cursorPosition);
662 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
663 cx
, tPtr
->offsetWidth
, cx
,
664 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
666 if (tPtr
->flags
.secure
)
673 drawRelief(WMView
*view
, Bool beveled
)
675 WMScreen
*scr
= view
->screen
;
676 Display
*dpy
= scr
->display
;
680 int width
= view
->size
.width
;
681 int height
= view
->size
.height
;
683 dgc
= WMColorGC(scr
->darkGray
);
686 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
690 wgc
= WMColorGC(scr
->white
);
691 lgc
= WMColorGC(scr
->gray
);
694 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
695 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
697 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
698 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
701 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
702 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
704 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
705 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
710 paintTextField(TextField
*tPtr
)
712 W_Screen
*screen
= tPtr
->view
->screen
;
713 W_View
*view
= tPtr
->view
;
723 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
726 if (!tPtr
->flags
.bordered
) {
732 if (tPtr
->flags
.secure
) {
733 text
= makeHiddenString(strlen(tPtr
->text
));
738 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
740 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
741 view
->size
.width
, view
->size
.height
, screen
->depth
);
742 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
743 0,0, view
->size
.width
,view
->size
.height
);
744 /* this is quite dirty */
745 viewbuffer
.screen
= view
->screen
;
746 viewbuffer
.size
= view
->size
;
747 viewbuffer
.window
= drawbuffer
;
750 if (tPtr
->textLen
> 0) {
751 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
752 tPtr
->textLen
- tPtr
->viewPosition
);
754 th
= WMFontHeight(tPtr
->font
);
756 ty
= tPtr
->offsetWidth
;
757 switch (tPtr
->flags
.alignment
) {
759 tx
= tPtr
->offsetWidth
+ 1;
760 if (tw
< tPtr
->usableWidth
)
761 XFillRectangle(screen
->display
, drawbuffer
,
762 WMColorGC(screen
->white
),
763 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
767 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
768 if (tw
< tPtr
->usableWidth
)
769 XClearArea(screen
->display
, view
->window
, bd
, bd
,
770 totalWidth
, view
->size
.height
-2*bd
, False
);
775 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
776 if (tw
< tPtr
->usableWidth
)
777 XClearArea(screen
->display
, view
->window
, bd
, bd
,
778 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
782 if (!tPtr
->flags
.enabled
)
783 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
785 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
787 &(text
[tPtr
->viewPosition
]),
788 tPtr
->textLen
- tPtr
->viewPosition
);
790 if (tPtr
->selection
.count
) {
793 count
= tPtr
->selection
.count
< 0
794 ? tPtr
->selection
.position
+ tPtr
->selection
.count
795 : tPtr
->selection
.position
;
796 count2
= abs(tPtr
->selection
.count
);
797 if (count
< tPtr
->viewPosition
) {
798 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
799 count
= tPtr
->viewPosition
;
803 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
804 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
806 XSetBackground(screen
->display
, screen
->textFieldGC
,
807 screen
->gray
->color
.pixel
);
809 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
810 tPtr
->font
, rx
, ty
, &(text
[count
]),
813 XSetBackground(screen
->display
, screen
->textFieldGC
,
814 screen
->white
->color
.pixel
);
817 if (!tPtr
->flags
.enabled
)
818 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
820 XFillRectangle(screen
->display
, drawbuffer
,
821 WMColorGC(screen
->white
),
822 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
826 if (tPtr
->flags
.bordered
) {
827 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
830 if (tPtr
->flags
.secure
)
832 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
833 screen
->copyGC
, 0,0, view
->size
.width
,
834 view
->size
.height
,0,0);
835 XFreePixmap(screen
->display
, drawbuffer
);
838 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
846 blinkCursor(void *data
)
848 TextField
*tPtr
= (TextField
*)data
;
850 if (tPtr
->flags
.cursorOn
) {
851 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
854 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
858 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
864 handleEvents(XEvent
*event
, void *data
)
866 TextField
*tPtr
= (TextField
*)data
;
868 CHECK_CLASS(data
, WC_TextField
);
871 switch (event
->type
) {
873 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
875 tPtr
->flags
.focused
= 1;
877 if (!tPtr
->timerID
) {
878 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
882 paintTextField(tPtr
);
884 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
886 tPtr
->flags
.notIllegalMovement
= 0;
890 tPtr
->flags
.focused
= 0;
893 WMDeleteTimerHandler(tPtr
->timerID
);
894 tPtr
->timerID
= NULL
;
897 paintTextField(tPtr
);
898 if (!tPtr
->flags
.notIllegalMovement
) {
899 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
900 (void*)WMIllegalTextMovement
);
905 if (event
->xexpose
.count
!=0)
907 paintTextField(tPtr
);
911 destroyTextField(tPtr
);
918 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
922 int count
, refresh
= 0;
923 int control_pressed
= 0;
925 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
) {
929 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
930 buffer
[count
] = '\0';
932 if (!(event
->xkey
.state
& ShiftMask
)) {
933 if (tPtr
->selection
.count
)
935 tPtr
->prevselection
= tPtr
->selection
;
936 tPtr
->selection
.position
= tPtr
->cursorPosition
;
937 tPtr
->selection
.count
= 0;
940 /* Be careful in any case in this switch statement, never to call
941 * to more than a function that can generate text change notifications.
942 * Only one text change notification should be sent in any case.
943 * Else hazardous things can happen.
944 * Maybe we need a better solution than the function wrapper to inform
945 * functions that change text in text fields, if they need to send a
946 * change notification or not. -Dan
950 case XK_ISO_Left_Tab
:
951 if (event
->xkey
.state
& ShiftMask
) {
952 if (tPtr
->view
->prevFocusChain
) {
953 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
954 tPtr
->view
->prevFocusChain
);
955 tPtr
->flags
.notIllegalMovement
= 1;
957 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
958 (void*)WMBacktabTextMovement
);
960 if (tPtr
->view
->nextFocusChain
) {
961 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
962 tPtr
->view
->nextFocusChain
);
963 tPtr
->flags
.notIllegalMovement
= 1;
965 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
966 (void*)WMTabTextMovement
);
971 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
972 (void*)WMReturnTextMovement
);
975 case WM_EMACSKEY_LEFT
:
976 if (!control_pressed
) {
981 if (tPtr
->cursorPosition
> 0) {
983 if (event
->xkey
.state
& ControlMask
) {
985 for (i
= tPtr
->cursorPosition
- 1; i
>= 0; i
--)
986 if (tPtr
->text
[i
] == ' ' || i
== 0) {
987 tPtr
->cursorPosition
= i
;
991 tPtr
->cursorPosition
--;
993 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
994 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1002 case WM_EMACSKEY_RIGHT
:
1003 if (!control_pressed
) {
1008 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1010 if (event
->xkey
.state
& ControlMask
) {
1012 for (i
= tPtr
->cursorPosition
+ 1; i
<= tPtr
->textLen
; i
++)
1013 if (tPtr
->text
[i
] == ' ' || i
== tPtr
->textLen
) {
1014 tPtr
->cursorPosition
= i
;
1018 tPtr
->cursorPosition
++;
1020 while (WMWidthOfString(tPtr
->font
,
1021 &(tPtr
->text
[tPtr
->viewPosition
]),
1022 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1023 > tPtr
->usableWidth
) {
1024 tPtr
->viewPosition
++;
1032 case WM_EMACSKEY_HOME
:
1033 if (!control_pressed
) {
1038 if (tPtr
->cursorPosition
> 0) {
1040 tPtr
->cursorPosition
= 0;
1041 if (tPtr
->viewPosition
> 0) {
1042 tPtr
->viewPosition
= 0;
1050 case WM_EMACSKEY_END
:
1051 if (!control_pressed
) {
1056 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1058 tPtr
->cursorPosition
= tPtr
->textLen
;
1059 tPtr
->viewPosition
= 0;
1060 while (WMWidthOfString(tPtr
->font
,
1061 &(tPtr
->text
[tPtr
->viewPosition
]),
1062 tPtr
->textLen
-tPtr
->viewPosition
)
1063 > tPtr
->usableWidth
) {
1064 tPtr
->viewPosition
++;
1072 case WM_EMACSKEY_BS
:
1073 if (!control_pressed
) {
1077 if (tPtr
->cursorPosition
> 0) {
1080 if (tPtr
->prevselection
.count
) {
1081 range
.position
= tPtr
->prevselection
.count
< 0
1082 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1083 : tPtr
->prevselection
.position
;
1085 range
.count
= abs(tPtr
->prevselection
.count
);
1087 range
.position
= tPtr
->cursorPosition
- 1;
1090 WMDeleteTextFieldRange(tPtr
, range
);
1091 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1092 (void*)WMDeleteTextEvent
);
1096 case WM_EMACSKEY_DEL
:
1097 if (!control_pressed
) {
1102 if (tPtr
->cursorPosition
< tPtr
->textLen
|| tPtr
->prevselection
.count
) {
1105 if (tPtr
->prevselection
.count
) {
1106 range
.position
= tPtr
->prevselection
.count
< 0
1107 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1108 : tPtr
->prevselection
.position
;
1110 range
.count
= abs(tPtr
->prevselection
.count
);
1112 range
.position
= tPtr
->cursorPosition
;
1115 WMDeleteTextFieldRange(tPtr
, range
);
1116 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1117 (void*)WMDeleteTextEvent
);
1123 if (count
> 0 && !iscntrl(buffer
[0])) {
1126 if (tPtr
->prevselection
.count
) {
1127 range
.position
= tPtr
->prevselection
.count
< 0
1128 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1129 : tPtr
->prevselection
.position
;
1131 range
.count
= abs(tPtr
->prevselection
.count
);
1133 range
.position
= tPtr
->cursorPosition
;
1136 if (tPtr
->prevselection
.count
)
1137 WMDeleteTextFieldRange(tPtr
, range
);
1138 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1139 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1140 (void*)WMInsertTextEvent
);
1146 if (event
->xkey
.state
& ShiftMask
) {
1147 if (tPtr
->selection
.count
== 0)
1148 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1149 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1152 tPtr
->prevselection
.count
= 0;
1154 paintTextField(tPtr
);
1160 pointToCursorPosition(TextField
*tPtr
, int x
)
1165 if (tPtr
->flags
.bordered
)
1168 a
= tPtr
->viewPosition
;
1169 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1170 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1171 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1172 return tPtr
->textLen
;
1174 while (a
< b
&& b
-a
>1) {
1176 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1177 mid
- tPtr
->viewPosition
);
1190 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1192 TextField
*tPtr
= (TextField
*)data
;
1195 CHECK_CLASS(data
, WC_TextField
);
1197 switch (event
->type
) {
1199 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1200 handleTextFieldKeyPress(tPtr
, event
);
1201 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1202 W_VIEW(tPtr
)->window
, False
,
1203 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1204 GrabModeAsync
, GrabModeAsync
, None
,
1205 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1207 tPtr
->flags
.pointerGrabbed
= 1;
1213 if (tPtr
->flags
.pointerGrabbed
) {
1214 tPtr
->flags
.pointerGrabbed
= 0;
1215 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1218 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1220 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1221 tPtr
->usableWidth
) {
1222 if (WMWidthOfString(tPtr
->font
,
1223 &(tPtr
->text
[tPtr
->viewPosition
]),
1224 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1225 > tPtr
->usableWidth
) {
1226 tPtr
->viewPosition
++;
1228 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1230 tPtr
->viewPosition
--;
1233 if (!tPtr
->selection
.count
) {
1234 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1237 tPtr
->cursorPosition
=
1238 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1240 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1243 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1247 paintTextField(tPtr
);
1251 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1252 XA_PRIMARY
, tPtr
->view
->window
, CurrentTime
);
1254 WMNotification
*notif
= WMCreateNotification("_lostOwnership",
1257 WMPostNotification(notif
);
1258 WMReleaseNotification(notif
);
1264 if (tPtr
->flags
.pointerGrabbed
) {
1265 tPtr
->flags
.pointerGrabbed
= 0;
1266 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1271 switch (tPtr
->flags
.alignment
) {
1274 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1275 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1276 WMSetFocusToWidget(tPtr
);
1277 } else if (tPtr
->flags
.focused
) {
1278 tPtr
->selection
.count
= 0;
1280 if(textWidth
< tPtr
->usableWidth
){
1281 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1282 event
->xbutton
.x
- tPtr
->usableWidth
1285 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1288 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1290 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1293 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1296 paintTextField(tPtr
);
1300 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1301 WMSetFocusToWidget(tPtr
);
1302 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1304 paintTextField(tPtr
);
1305 } else if (tPtr
->flags
.focused
) {
1306 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1308 tPtr
->selection
.count
= 0;
1309 paintTextField(tPtr
);
1311 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1314 text
= W_GetTextSelection(tPtr
->view
->screen
, XA_PRIMARY
);
1317 text
= W_GetTextSelection(tPtr
->view
->screen
,
1318 tPtr
->view
->screen
->clipboardAtom
);
1321 text
= W_GetTextSelection(tPtr
->view
->screen
,
1325 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1327 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1328 (void*)WMInsertTextEvent
);
1336 if (tPtr
->flags
.pointerGrabbed
) {
1337 tPtr
->flags
.pointerGrabbed
= 0;
1338 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1348 destroyTextField(TextField
*tPtr
)
1352 WMDeleteTimerHandler(tPtr
->timerID
);
1355 WMReleaseFont(tPtr
->font
);
1356 WMDeleteSelectionHandler(tPtr
, XA_PRIMARY
);
1357 WMRemoveNotificationObserver(tPtr
);