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 */
46 WMTextFieldDelegate
*delegate
;
49 WMHandlerID timerID
; /* for cursor blinking */
52 WMAlignment alignment
:2;
54 unsigned int bordered
:1;
56 unsigned int beveled
:1;
58 unsigned int enabled
:1;
60 unsigned int focused
:1;
62 unsigned int cursorOn
:1;
64 unsigned int secure
:1; /* password entry style */
66 unsigned int pointerGrabbed
:1;
68 unsigned int ownsSelection
:1;
70 unsigned int waitingSelection
:1; /* requested selection, but
74 unsigned int notIllegalMovement
:1;
79 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
80 if ((T)->delegate && (T)->delegate->C)\
81 (*(T)->delegate->C)((T)->delegate,notif);\
82 WMPostNotification(notif);\
83 WMReleaseNotification(notif);}
86 #define MIN_TEXT_BUFFER 2
87 #define TEXT_BUFFER_INCR 8
90 #define WM_EMACSKEYMASK ControlMask
92 #define WM_EMACSKEY_LEFT XK_b
93 #define WM_EMACSKEY_RIGHT XK_f
94 #define WM_EMACSKEY_HOME XK_a
95 #define WM_EMACSKEY_END XK_e
96 #define WM_EMACSKEY_BS XK_h
97 #define WM_EMACSKEY_DEL XK_d
101 #define DEFAULT_WIDTH 60
102 #define DEFAULT_HEIGHT 20
103 #define DEFAULT_BORDERED True
104 #define DEFAULT_ALIGNMENT WALeft
108 static void destroyTextField(TextField
*tPtr
);
109 static void paintTextField(TextField
*tPtr
);
111 static void handleEvents(XEvent
*event
, void *data
);
112 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
113 static void didResizeTextField();
115 struct W_ViewDelegate _TextFieldViewDelegate
= {
125 static void lostHandler(WMView
*view
, Atom selection
, void *cdata
);
127 static WMData
*requestHandler(WMView
*view
, Atom selection
, Atom target
,
128 void *cdata
, Atom
*type
);
131 static WMSelectionProcs selectionHandler
= {
138 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
139 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
141 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
142 &((tPtr)->text[(start)]), (end) - (start) + 1))
146 normalizeRange(TextField
*tPtr
, WMRange
*range
)
148 if (range
->position
< 0 && range
->count
< 0)
151 if (range
->count
== 0) {
152 /*range->position = 0; why is this?*/
156 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
157 if (range
->count
< 0) { /* && range->position >= 0 */
158 if (range
->position
+ range
->count
< 0) {
159 range
->count
= range
->position
;
162 range
->count
= -range
->count
;
163 range
->position
-= range
->count
;
165 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
166 } else if (range
->position
< 0) { /* && range->count > 0 */
167 if (range
->position
+ range
->count
< 0) {
168 range
->position
= range
->count
= 0;
170 range
->count
+= range
->position
;
175 if (range
->position
+ range
->count
> tPtr
->textLen
)
176 range
->count
= tPtr
->textLen
- range
->position
;
180 memmv(char *dest
, char *src
, int size
)
185 for (i
=size
-1; i
>=0; i
--) {
188 } else if (dest
< src
) {
189 for (i
=0; i
<size
; i
++) {
197 incrToFit(TextField
*tPtr
)
199 int vp
= tPtr
->viewPosition
;
201 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
202 tPtr
->viewPosition
++;
204 return vp
!=tPtr
->viewPosition
;
209 incrToFit2(TextField
*tPtr
)
211 int vp
= tPtr
->viewPosition
;
212 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
213 >= tPtr
->usableWidth
)
214 tPtr
->viewPosition
++;
217 return vp
!=tPtr
->viewPosition
;
222 decrToFit(TextField
*tPtr
)
224 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
225 && tPtr
->viewPosition
>0)
226 tPtr
->viewPosition
--;
235 requestHandler(WMView
*view
, Atom selection
, Atom target
, void *cdata
,
238 TextField
*tPtr
= view
->self
;
240 Display
*dpy
= tPtr
->view
->screen
->display
;
242 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
243 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
246 count
= tPtr
->selection
.count
< 0
247 ? tPtr
->selection
.position
+ tPtr
->selection
.count
248 : tPtr
->selection
.position
;
250 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
252 data
= WMCreateDataWithBytes(&(tPtr
->text
[count
]),
253 abs(tPtr
->selection
.count
));
254 WMSetDataFormat(data
, 8);
260 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
261 if (target
== _TARGETS
) {
264 ptr
= wmalloc(4 * sizeof(Atom
));
268 ptr
[3] = COMPOUND_TEXT
;
270 data
= WMCreateDataWithBytes(ptr
, 4*4);
271 WMSetDataFormat(data
, 32);
283 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
285 TextField
*tPtr
= (WMTextField
*)view
->self
;
287 tPtr
->flags
.ownsSelection
= 0;
288 tPtr
->selection
.count
= 0;
289 paintTextField(tPtr
);
294 _notification(void *observerData
, WMNotification
*notification
)
296 WMTextField
*to
= (WMTextField
*)observerData
;
297 WMTextField
*tw
= (WMTextField
*)WMGetNotificationClientData(notification
);
298 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
303 WMCreateTextField(WMWidget
*parent
)
307 tPtr
= wmalloc(sizeof(TextField
));
308 memset(tPtr
, 0, sizeof(TextField
));
310 tPtr
->widgetClass
= WC_TextField
;
312 tPtr
->view
= W_CreateView(W_VIEW(parent
));
317 tPtr
->view
->self
= tPtr
;
319 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
321 tPtr
->view
->attribFlags
|= CWCursor
;
322 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
324 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
326 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
329 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
331 tPtr
->flags
.enabled
= 1;
333 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
334 |FocusChangeMask
, handleEvents
, tPtr
);
336 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
338 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
339 tPtr
->flags
.beveled
= True
;
340 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
342 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
344 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
346 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
347 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
348 handleTextFieldActionEvents
, tPtr
);
350 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
353 tPtr
->flags
.cursorOn
= 1;
360 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
362 CHECK_CLASS(tPtr
, WC_TextField
);
364 tPtr
->delegate
= delegate
;
369 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
373 CHECK_CLASS(tPtr
, WC_TextField
);
380 /* check if buffer will hold the text */
381 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
382 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
383 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
386 if (position
< 0 || position
>= tPtr
->textLen
) {
387 /* append the text at the end */
388 strcat(tPtr
->text
, text
);
392 tPtr
->textLen
+= len
;
393 tPtr
->cursorPosition
+= len
;
395 /* insert text at position */
396 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
397 tPtr
->textLen
-position
+1);
399 memcpy(&(tPtr
->text
[position
]), text
, len
);
401 tPtr
->textLen
+= len
;
402 if (position
>= tPtr
->cursorPosition
) {
403 tPtr
->cursorPosition
+= len
;
410 paintTextField(tPtr
);
414 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
416 CHECK_CLASS(tPtr
, WC_TextField
);
418 normalizeRange(tPtr
, &range
);
423 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
424 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
426 tPtr
->textLen
-= range
.count
;
428 /* try to keep cursorPosition at the same place */
429 tPtr
->viewPosition
-= range
.count
;
430 if (tPtr
->viewPosition
< 0)
431 tPtr
->viewPosition
= 0;
432 tPtr
->cursorPosition
= range
.position
;
436 paintTextField(tPtr
);
442 WMGetTextFieldText(WMTextField
*tPtr
)
444 CHECK_CLASS(tPtr
, WC_TextField
);
446 return wstrdup(tPtr
->text
);
451 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
453 CHECK_CLASS(tPtr
, WC_TextField
);
455 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
456 (!text
&& tPtr
->textLen
== 0))
463 tPtr
->textLen
= strlen(text
);
465 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
466 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
467 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
469 strcpy(tPtr
->text
, text
);
472 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
473 tPtr
->viewPosition
= 0;
474 tPtr
->selection
.count
= 0;
476 if (tPtr
->view
->flags
.realized
)
477 paintTextField(tPtr
);
482 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
484 CHECK_CLASS(tPtr
, WC_TextField
);
486 tPtr
->flags
.alignment
= alignment
;
488 if (alignment
!=WALeft
) {
489 wwarning("only left alignment is supported in textfields");
493 if (tPtr
->view
->flags
.realized
) {
494 paintTextField(tPtr
);
500 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
502 CHECK_CLASS(tPtr
, WC_TextField
);
504 tPtr
->flags
.bordered
= bordered
;
506 if (tPtr
->view
->flags
.realized
) {
507 paintTextField(tPtr
);
513 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
515 CHECK_CLASS(tPtr
, WC_TextField
);
517 tPtr
->flags
.beveled
= flag
;
519 if (tPtr
->view
->flags
.realized
) {
520 paintTextField(tPtr
);
527 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
529 CHECK_CLASS(tPtr
, WC_TextField
);
531 tPtr
->flags
.secure
= flag
;
533 if (tPtr
->view
->flags
.realized
) {
534 paintTextField(tPtr
);
540 WMGetTextFieldEditable(WMTextField
*tPtr
)
542 CHECK_CLASS(tPtr
, WC_TextField
);
544 return tPtr
->flags
.enabled
;
549 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
551 CHECK_CLASS(tPtr
, WC_TextField
);
553 tPtr
->flags
.enabled
= flag
;
555 if (tPtr
->view
->flags
.realized
) {
556 paintTextField(tPtr
);
562 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
564 CHECK_CLASS(tPtr
, WC_TextField
);
566 if (tPtr
->flags
.enabled
) {
567 normalizeRange(tPtr
, &range
);
569 tPtr
->selection
= range
;
571 tPtr
->cursorPosition
= range
.position
+ range
.count
;
573 if (tPtr
->view
->flags
.realized
) {
574 paintTextField(tPtr
);
581 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
583 CHECK_CLASS(tPtr
, WC_TextField
);
585 if (tPtr
->flags
.enabled
) {
586 if (position
> tPtr
->textLen
)
587 position
= tPtr
->textLen
;
589 tPtr
->cursorPosition
= position
;
590 if (tPtr
->view
->flags
.realized
) {
591 paintTextField(tPtr
);
598 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
600 CHECK_CLASS(tPtr
, WC_TextField
);
602 if (tPtr
->view
->nextFocusChain
)
603 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
604 tPtr
->view
->nextFocusChain
= NULL
;
608 CHECK_CLASS(next
, WC_TextField
);
610 if (tPtr
->view
->nextFocusChain
)
611 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
612 if (next
->view
->prevFocusChain
)
613 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
615 tPtr
->view
->nextFocusChain
= next
->view
;
616 next
->view
->prevFocusChain
= tPtr
->view
;
621 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
623 CHECK_CLASS(tPtr
, WC_TextField
);
625 if (tPtr
->view
->prevFocusChain
)
626 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
627 tPtr
->view
->prevFocusChain
= NULL
;
631 CHECK_CLASS(prev
, WC_TextField
);
633 if (tPtr
->view
->prevFocusChain
)
634 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
635 if (prev
->view
->nextFocusChain
)
636 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
638 tPtr
->view
->prevFocusChain
= prev
->view
;
639 prev
->view
->nextFocusChain
= tPtr
->view
;
644 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
646 CHECK_CLASS(tPtr
, WC_TextField
);
649 WMReleaseFont(tPtr
->font
);
650 tPtr
->font
= WMRetainFont(font
);
653 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
655 if (tPtr
->view
->flags
.realized
) {
656 paintTextField(tPtr
);
663 WMGetTextFieldFont(WMTextField
*tPtr
)
670 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
672 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
675 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
677 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
682 makeHiddenString(int length
)
684 char *data
= wmalloc(length
+1);
686 memset(data
, '*', length
);
694 paintCursor(TextField
*tPtr
)
697 WMScreen
*screen
= tPtr
->view
->screen
;
701 if (tPtr
->flags
.secure
)
702 text
= makeHiddenString(strlen(tPtr
->text
));
706 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
707 tPtr
->cursorPosition
-tPtr
->viewPosition
);
709 switch (tPtr
->flags
.alignment
) {
711 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
712 if (textWidth
< tPtr
->usableWidth
)
713 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
715 cx
+= tPtr
->offsetWidth
+ 1;
718 cx
+= tPtr
->offsetWidth
+ 1;
723 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
724 if (textWidth
< tPtr
->usableWidth
)
725 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
727 cx
+= tPtr
->offsetWidth
;
731 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
732 cx, tPtr->offsetWidth, 1,
733 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
734 printf("%d %d\n",cx,tPtr->cursorPosition);
736 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
737 cx
, tPtr
->offsetWidth
, cx
,
738 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
740 if (tPtr
->flags
.secure
)
747 drawRelief(WMView
*view
, Bool beveled
)
749 WMScreen
*scr
= view
->screen
;
750 Display
*dpy
= scr
->display
;
754 int width
= view
->size
.width
;
755 int height
= view
->size
.height
;
757 dgc
= WMColorGC(scr
->darkGray
);
760 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
764 wgc
= WMColorGC(scr
->white
);
765 lgc
= WMColorGC(scr
->gray
);
768 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
769 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
771 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
772 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
775 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
776 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
778 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
779 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
784 paintTextField(TextField
*tPtr
)
786 W_Screen
*screen
= tPtr
->view
->screen
;
787 W_View
*view
= tPtr
->view
;
797 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
800 if (!tPtr
->flags
.bordered
) {
806 if (tPtr
->flags
.secure
) {
807 text
= makeHiddenString(strlen(tPtr
->text
));
812 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
814 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
815 view
->size
.width
, view
->size
.height
, screen
->depth
);
816 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
817 0,0, view
->size
.width
,view
->size
.height
);
818 /* this is quite dirty */
819 viewbuffer
.screen
= view
->screen
;
820 viewbuffer
.size
= view
->size
;
821 viewbuffer
.window
= drawbuffer
;
824 if (tPtr
->textLen
> 0) {
825 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
826 tPtr
->textLen
- tPtr
->viewPosition
);
828 th
= WMFontHeight(tPtr
->font
);
830 ty
= tPtr
->offsetWidth
;
831 switch (tPtr
->flags
.alignment
) {
833 tx
= tPtr
->offsetWidth
+ 1;
834 if (tw
< tPtr
->usableWidth
)
835 XFillRectangle(screen
->display
, drawbuffer
,
836 WMColorGC(screen
->white
),
837 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
841 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
842 if (tw
< tPtr
->usableWidth
)
843 XClearArea(screen
->display
, view
->window
, bd
, bd
,
844 totalWidth
, view
->size
.height
-2*bd
, False
);
849 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
850 if (tw
< tPtr
->usableWidth
)
851 XClearArea(screen
->display
, view
->window
, bd
, bd
,
852 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
856 if (!tPtr
->flags
.enabled
)
857 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
859 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
861 &(text
[tPtr
->viewPosition
]),
862 tPtr
->textLen
- tPtr
->viewPosition
);
864 if (tPtr
->selection
.count
) {
867 count
= tPtr
->selection
.count
< 0
868 ? tPtr
->selection
.position
+ tPtr
->selection
.count
869 : tPtr
->selection
.position
;
870 count2
= abs(tPtr
->selection
.count
);
871 if (count
< tPtr
->viewPosition
) {
872 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
873 count
= tPtr
->viewPosition
;
877 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
878 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
880 XSetBackground(screen
->display
, screen
->textFieldGC
,
881 screen
->gray
->color
.pixel
);
883 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
884 tPtr
->font
, rx
, ty
, &(text
[count
]),
887 XSetBackground(screen
->display
, screen
->textFieldGC
,
888 screen
->white
->color
.pixel
);
891 if (!tPtr
->flags
.enabled
)
892 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
894 XFillRectangle(screen
->display
, drawbuffer
,
895 WMColorGC(screen
->white
),
896 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
900 if (tPtr
->flags
.bordered
) {
901 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
904 if (tPtr
->flags
.secure
)
906 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
907 screen
->copyGC
, 0,0, view
->size
.width
,
908 view
->size
.height
,0,0);
909 XFreePixmap(screen
->display
, drawbuffer
);
912 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
920 blinkCursor(void *data
)
922 TextField
*tPtr
= (TextField
*)data
;
924 if (tPtr
->flags
.cursorOn
) {
925 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
928 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
932 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
938 handleEvents(XEvent
*event
, void *data
)
940 TextField
*tPtr
= (TextField
*)data
;
942 CHECK_CLASS(data
, WC_TextField
);
945 switch (event
->type
) {
947 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
949 tPtr
->flags
.focused
= 1;
951 if (!tPtr
->timerID
) {
952 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
956 paintTextField(tPtr
);
958 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
960 tPtr
->flags
.notIllegalMovement
= 0;
964 tPtr
->flags
.focused
= 0;
967 WMDeleteTimerHandler(tPtr
->timerID
);
968 tPtr
->timerID
= NULL
;
971 paintTextField(tPtr
);
972 if (!tPtr
->flags
.notIllegalMovement
) {
973 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
974 (void*)WMIllegalTextMovement
);
979 if (event
->xexpose
.count
!=0)
981 paintTextField(tPtr
);
985 destroyTextField(tPtr
);
992 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
996 char *textEvent
= NULL
;
998 int count
, refresh
= 0;
999 int control_pressed
= 0;
1000 int cancelSelection
= 1;
1002 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1003 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1004 control_pressed
= 1;
1006 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
1007 buffer
[count
] = '\0';
1011 #ifdef XK_ISO_Left_Tab
1012 case XK_ISO_Left_Tab
:
1014 if (event
->xkey
.state
& ShiftMask
) {
1015 if (tPtr
->view
->prevFocusChain
) {
1016 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1017 tPtr
->view
->prevFocusChain
);
1018 tPtr
->flags
.notIllegalMovement
= 1;
1020 data
= (void*)WMBacktabTextMovement
;
1022 if (tPtr
->view
->nextFocusChain
) {
1023 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1024 tPtr
->view
->nextFocusChain
);
1025 tPtr
->flags
.notIllegalMovement
= 1;
1027 data
= (void*)WMTabTextMovement
;
1029 textEvent
= WMTextDidEndEditingNotification
;
1033 data
= (void*)WMEscapeTextMovement
;
1034 textEvent
= WMTextDidEndEditingNotification
;
1038 data
= (void*)WMReturnTextMovement
;
1039 textEvent
= WMTextDidEndEditingNotification
;
1042 case WM_EMACSKEY_LEFT
:
1043 if (!control_pressed
) {
1050 if (tPtr
->cursorPosition
> 0) {
1052 if (event
->xkey
.state
& ControlMask
) {
1053 int i
= tPtr
->cursorPosition
- 1;
1055 while (i
> 0 && tPtr
->text
[i
] != ' ') i
--;
1056 while (i
> 0 && tPtr
->text
[i
] == ' ') i
--;
1058 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1060 tPtr
->cursorPosition
--;
1062 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1063 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1068 if (event
->xkey
.state
& ShiftMask
)
1069 cancelSelection
= 0;
1072 case WM_EMACSKEY_RIGHT
:
1073 if (!control_pressed
) {
1080 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1082 if (event
->xkey
.state
& ControlMask
) {
1083 int i
= tPtr
->cursorPosition
;
1085 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ') i
++;
1086 while (tPtr
->text
[i
] == ' ') i
++;
1088 tPtr
->cursorPosition
= i
;
1090 tPtr
->cursorPosition
++;
1092 while (WMWidthOfString(tPtr
->font
,
1093 &(tPtr
->text
[tPtr
->viewPosition
]),
1094 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1095 > tPtr
->usableWidth
) {
1096 tPtr
->viewPosition
++;
1102 if (event
->xkey
.state
& ShiftMask
)
1103 cancelSelection
= 0;
1106 case WM_EMACSKEY_HOME
:
1107 if (!control_pressed
) {
1114 if (tPtr
->cursorPosition
> 0) {
1116 tPtr
->cursorPosition
= 0;
1117 if (tPtr
->viewPosition
> 0) {
1118 tPtr
->viewPosition
= 0;
1123 if (event
->xkey
.state
& ShiftMask
)
1124 cancelSelection
= 0;
1127 case WM_EMACSKEY_END
:
1128 if (!control_pressed
) {
1135 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1137 tPtr
->cursorPosition
= tPtr
->textLen
;
1138 tPtr
->viewPosition
= 0;
1139 while (WMWidthOfString(tPtr
->font
,
1140 &(tPtr
->text
[tPtr
->viewPosition
]),
1141 tPtr
->textLen
-tPtr
->viewPosition
)
1142 > tPtr
->usableWidth
) {
1143 tPtr
->viewPosition
++;
1149 if (event
->xkey
.state
& ShiftMask
)
1150 cancelSelection
= 0;
1153 case WM_EMACSKEY_BS
:
1154 if (!control_pressed
) {
1158 if (tPtr
->selection
.count
) {
1159 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1160 data
= (void*)WMDeleteTextEvent
;
1161 textEvent
= WMTextDidChangeNotification
;
1162 } else if (tPtr
->cursorPosition
> 0) {
1164 range
.position
= tPtr
->cursorPosition
- 1;
1166 WMDeleteTextFieldRange(tPtr
, range
);
1167 data
= (void*)WMDeleteTextEvent
;
1168 textEvent
= WMTextDidChangeNotification
;
1172 case WM_EMACSKEY_DEL
:
1173 if (!control_pressed
) {
1180 if (tPtr
->selection
.count
) {
1181 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1182 data
= (void*)WMDeleteTextEvent
;
1183 textEvent
= WMTextDidChangeNotification
;
1184 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1186 range
.position
= tPtr
->cursorPosition
;
1188 WMDeleteTextFieldRange(tPtr
, range
);
1189 data
= (void*)WMDeleteTextEvent
;
1190 textEvent
= WMTextDidChangeNotification
;
1196 if (count
> 0 && isprint(buffer
[0])) {
1197 if (tPtr
->selection
.count
)
1198 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1199 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1200 data
= (void*)WMInsertTextEvent
;
1201 textEvent
= WMTextDidChangeNotification
;
1203 /* should we rather break and goto cancel selection below? -Dan */
1209 if (!cancelSelection
) {
1210 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1212 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1214 WMPostNotificationName("_lostOwnership", NULL
, tPtr
);
1220 lostHandler(tPtr
->view
, XA_PRIMARY
, NULL
);
1222 if (tPtr
->selection
.count
) {
1223 tPtr
->selection
.count
= 0;
1226 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1229 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1232 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1234 if (tPtr
->delegate
) {
1235 if (textEvent
==WMTextDidBeginEditingNotification
&&
1236 tPtr
->delegate
->didBeginEditing
)
1237 (*tPtr
->delegate
->didBeginEditing
)(tPtr
->delegate
, notif
);
1239 else if (textEvent
==WMTextDidEndEditingNotification
&&
1240 tPtr
->delegate
->didEndEditing
)
1241 (*tPtr
->delegate
->didEndEditing
)(tPtr
->delegate
, notif
);
1243 else if (textEvent
==WMTextDidChangeNotification
&&
1244 tPtr
->delegate
->didChange
)
1245 (*tPtr
->delegate
->didChange
)(tPtr
->delegate
, notif
);
1248 WMPostNotification(notif
);
1249 WMReleaseNotification(notif
);
1253 paintTextField(tPtr
);
1255 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1260 pointToCursorPosition(TextField
*tPtr
, int x
)
1265 if (tPtr
->flags
.bordered
)
1268 a
= tPtr
->viewPosition
;
1269 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1270 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1271 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1272 return tPtr
->textLen
;
1274 while (a
< b
&& b
-a
>1) {
1276 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1277 mid
- tPtr
->viewPosition
);
1291 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1292 void *cdata
, WMData
*data
)
1294 TextField
*tPtr
= (TextField
*)view
->self
;
1297 tPtr
->flags
.waitingSelection
= 0;
1300 str
= (char*)WMDataBytes(data
);
1302 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1303 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1304 (void*)WMInsertTextEvent
);
1308 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1312 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1314 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1315 (void*)WMInsertTextEvent
);
1322 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1324 TextField
*tPtr
= (TextField
*)data
;
1325 static int move
= 0;
1326 static Time lastButtonReleasedEvent
= 0;
1327 Display
*dpy
= event
->xany
.display
;
1329 CHECK_CLASS(data
, WC_TextField
);
1331 switch (event
->type
) {
1333 if (tPtr
->flags
.waitingSelection
) {
1336 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1337 handleTextFieldKeyPress(tPtr
, event
);
1338 XGrabPointer(dpy
, W_VIEW(tPtr
)->window
, False
,
1339 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1340 GrabModeAsync
, GrabModeAsync
, None
,
1341 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1343 tPtr
->flags
.pointerGrabbed
= 1;
1349 if (tPtr
->flags
.pointerGrabbed
) {
1350 tPtr
->flags
.pointerGrabbed
= 0;
1351 XUngrabPointer(dpy
, CurrentTime
);
1353 if (tPtr
->flags
.waitingSelection
) {
1357 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1359 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1360 tPtr
->usableWidth
) {
1361 if (WMWidthOfString(tPtr
->font
,
1362 &(tPtr
->text
[tPtr
->viewPosition
]),
1363 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1364 > tPtr
->usableWidth
) {
1365 tPtr
->viewPosition
++;
1367 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1369 tPtr
->viewPosition
--;
1372 tPtr
->cursorPosition
=
1373 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1375 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1377 if (tPtr
->selection
.count
!= 0) {
1378 if (!tPtr
->flags
.ownsSelection
) {
1379 WMCreateSelectionHandler(tPtr
->view
,
1381 event
->xbutton
.time
,
1382 &selectionHandler
, NULL
);
1383 tPtr
->flags
.ownsSelection
= 1;
1388 paintTextField(tPtr
);
1394 if (tPtr
->flags
.pointerGrabbed
) {
1395 tPtr
->flags
.pointerGrabbed
= 0;
1396 XUngrabPointer(dpy
, CurrentTime
);
1400 if (tPtr
->flags
.waitingSelection
) {
1405 switch (tPtr
->flags
.alignment
) {
1408 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1409 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1410 WMSetFocusToWidget(tPtr
);
1412 } else if (tPtr
->flags
.focused
) {
1413 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1414 tPtr
->selection
.count
= 0;
1416 if(textWidth
< tPtr
->usableWidth
) {
1417 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1418 event
->xbutton
.x
- tPtr
->usableWidth
1420 } else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1423 paintTextField(tPtr
);
1427 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1428 WMSetFocusToWidget(tPtr
);
1429 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1431 paintTextField(tPtr
);
1432 } else if (tPtr
->flags
.focused
1433 && event
->xbutton
.button
== Button1
) {
1434 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1436 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1437 tPtr
->selection
.count
= 0;
1438 paintTextField(tPtr
);
1440 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1444 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1445 event
->xbutton
.time
,
1447 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1451 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1453 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1454 (void*)WMInsertTextEvent
);
1457 tPtr
->flags
.waitingSelection
= 1;
1467 if (tPtr
->flags
.pointerGrabbed
) {
1468 tPtr
->flags
.pointerGrabbed
= 0;
1469 XUngrabPointer(dpy
, CurrentTime
);
1471 if (tPtr
->flags
.waitingSelection
) {
1475 if (tPtr
->selection
.count
!= 0) {
1477 XRotateBuffers(dpy
, 1);
1479 count
= abs(tPtr
->selection
.count
);
1480 if (tPtr
->selection
.count
< 0)
1481 start
= tPtr
->selection
.position
- count
;
1483 start
= tPtr
->selection
.position
;
1485 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1490 if (event
->xbutton
.time
- lastButtonReleasedEvent
1491 <= WINGsConfiguration
.doubleClickDelay
) {
1492 tPtr
->selection
.position
= 0;
1493 tPtr
->selection
.count
= tPtr
->textLen
;
1494 paintTextField(tPtr
);
1496 if (!tPtr
->flags
.ownsSelection
) {
1497 WMCreateSelectionHandler(tPtr
->view
,
1499 event
->xbutton
.time
,
1500 &selectionHandler
, NULL
);
1501 tPtr
->flags
.ownsSelection
= 1;
1504 WMPostNotificationName("_lostOwnership", NULL
, tPtr
);
1506 lastButtonReleasedEvent
= event
->xbutton
.time
;
1514 destroyTextField(TextField
*tPtr
)
1518 WMDeleteTimerHandler(tPtr
->timerID
);
1521 WMReleaseFont(tPtr
->font
);
1522 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1523 WMRemoveNotificationObserver(tPtr
);