8 #include <X11/keysym.h>
13 #define CURSOR_BLINK_ON_DELAY 600
14 #define CURSOR_BLINK_OFF_DELAY 300
18 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
19 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
20 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
23 typedef struct W_TextField
{
28 struct W_TextField
*nextField
; /* next textfield in the chain */
29 struct W_TextField
*prevField
;
33 int textLen
; /* size of text */
34 int bufferSize
; /* memory allocated for text */
36 int viewPosition
; /* position of text being shown */
38 int cursorPosition
; /* position of the insertion cursor */
41 short offsetWidth
; /* offset of text from border */
47 WMTextFieldDelegate
*delegate
;
50 WMHandlerID timerID
; /* for cursor blinking */
53 WMAlignment alignment
:2;
55 unsigned int bordered
:1;
57 unsigned int beveled
:1;
59 unsigned int enabled
:1;
61 unsigned int focused
:1;
63 unsigned int cursorOn
:1;
65 unsigned int secure
:1; /* password entry style */
67 unsigned int pointerGrabbed
:1;
69 unsigned int ownsSelection
:1;
71 unsigned int waitingSelection
:1; /* requested selection, but
75 unsigned int notIllegalMovement
:1;
80 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
81 if ((T)->delegate && (T)->delegate->C)\
82 (*(T)->delegate->C)((T)->delegate,notif);\
83 WMPostNotification(notif);\
84 WMReleaseNotification(notif);}
87 #define MIN_TEXT_BUFFER 2
88 #define TEXT_BUFFER_INCR 8
91 #define WM_EMACSKEYMASK ControlMask
93 #define WM_EMACSKEY_LEFT XK_b
94 #define WM_EMACSKEY_RIGHT XK_f
95 #define WM_EMACSKEY_HOME XK_a
96 #define WM_EMACSKEY_END XK_e
97 #define WM_EMACSKEY_BS XK_h
98 #define WM_EMACSKEY_DEL XK_d
102 #define DEFAULT_WIDTH 60
103 #define DEFAULT_HEIGHT 20
104 #define DEFAULT_BORDERED True
105 #define DEFAULT_ALIGNMENT WALeft
109 static void destroyTextField(TextField
*tPtr
);
110 static void paintTextField(TextField
*tPtr
);
112 static void handleEvents(XEvent
*event
, void *data
);
113 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
114 static void didResizeTextField();
116 struct W_ViewDelegate _TextFieldViewDelegate
= {
126 static void lostHandler(WMView
*view
, Atom selection
, void *cdata
);
128 static WMData
*requestHandler(WMView
*view
, Atom selection
, Atom target
,
129 void *cdata
, Atom
*type
);
132 static WMSelectionProcs selectionHandler
= {
139 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
140 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
142 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
143 &((tPtr)->text[(start)]), (end) - (start) + 1))
147 normalizeRange(TextField
*tPtr
, WMRange
*range
)
149 if (range
->position
< 0 && range
->count
< 0)
152 if (range
->count
== 0) {
153 /*range->position = 0; why is this?*/
157 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
158 if (range
->count
< 0) { /* && range->position >= 0 */
159 if (range
->position
+ range
->count
< 0) {
160 range
->count
= range
->position
;
163 range
->count
= -range
->count
;
164 range
->position
-= range
->count
;
166 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
167 } else if (range
->position
< 0) { /* && range->count > 0 */
168 if (range
->position
+ range
->count
< 0) {
169 range
->position
= range
->count
= 0;
171 range
->count
+= range
->position
;
176 if (range
->position
+ range
->count
> tPtr
->textLen
)
177 range
->count
= tPtr
->textLen
- range
->position
;
181 memmv(char *dest
, char *src
, int size
)
186 for (i
=size
-1; i
>=0; i
--) {
189 } else if (dest
< src
) {
190 for (i
=0; i
<size
; i
++) {
198 incrToFit(TextField
*tPtr
)
200 int vp
= tPtr
->viewPosition
;
202 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
203 tPtr
->viewPosition
++;
205 return vp
!=tPtr
->viewPosition
;
210 incrToFit2(TextField
*tPtr
)
212 int vp
= tPtr
->viewPosition
;
213 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
214 >= tPtr
->usableWidth
)
215 tPtr
->viewPosition
++;
218 return vp
!=tPtr
->viewPosition
;
223 decrToFit(TextField
*tPtr
)
225 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
226 && tPtr
->viewPosition
>0)
227 tPtr
->viewPosition
--;
236 requestHandler(WMView
*view
, Atom selection
, Atom target
, void *cdata
,
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 count
= tPtr
->selection
.count
< 0
248 ? tPtr
->selection
.position
+ tPtr
->selection
.count
249 : tPtr
->selection
.position
;
251 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
253 data
= WMCreateDataWithBytes(&(tPtr
->text
[count
]),
254 abs(tPtr
->selection
.count
));
255 WMSetDataFormat(data
, 8);
261 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
262 if (target
== _TARGETS
) {
265 ptr
= wmalloc(4 * sizeof(Atom
));
269 ptr
[3] = COMPOUND_TEXT
;
271 data
= WMCreateDataWithBytes(ptr
, 4*4);
272 WMSetDataFormat(data
, 32);
284 lostHandler(WMView
*view
, Atom selection
, void *cdata
)
286 TextField
*tPtr
= (WMTextField
*)view
->self
;
288 tPtr
->flags
.ownsSelection
= 0;
289 tPtr
->selection
.count
= 0;
290 paintTextField(tPtr
);
295 _notification(void *observerData
, WMNotification
*notification
)
297 WMTextField
*to
= (WMTextField
*)observerData
;
298 WMTextField
*tw
= (WMTextField
*)WMGetNotificationClientData(notification
);
299 if (to
!= tw
) lostHandler(to
->view
, XA_PRIMARY
, NULL
);
304 WMCreateTextField(WMWidget
*parent
)
308 tPtr
= wmalloc(sizeof(TextField
));
309 memset(tPtr
, 0, sizeof(TextField
));
311 tPtr
->widgetClass
= WC_TextField
;
313 tPtr
->view
= W_CreateView(W_VIEW(parent
));
318 tPtr
->view
->self
= tPtr
;
320 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
322 tPtr
->view
->attribFlags
|= CWCursor
;
323 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
325 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
327 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
330 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
332 tPtr
->flags
.enabled
= 1;
334 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
335 |FocusChangeMask
, handleEvents
, tPtr
);
337 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
339 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
340 tPtr
->flags
.beveled
= True
;
341 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
343 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
345 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
347 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
348 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
349 handleTextFieldActionEvents
, tPtr
);
351 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
354 tPtr
->flags
.cursorOn
= 1;
361 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
363 CHECK_CLASS(tPtr
, WC_TextField
);
365 tPtr
->delegate
= delegate
;
370 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
374 CHECK_CLASS(tPtr
, WC_TextField
);
381 /* check if buffer will hold the text */
382 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
383 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
384 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
387 if (position
< 0 || position
>= tPtr
->textLen
) {
388 /* append the text at the end */
389 strcat(tPtr
->text
, text
);
393 tPtr
->textLen
+= len
;
394 tPtr
->cursorPosition
+= len
;
396 /* insert text at position */
397 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
398 tPtr
->textLen
-position
+1);
400 memcpy(&(tPtr
->text
[position
]), text
, len
);
402 tPtr
->textLen
+= len
;
403 if (position
>= tPtr
->cursorPosition
) {
404 tPtr
->cursorPosition
+= len
;
411 paintTextField(tPtr
);
415 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
417 CHECK_CLASS(tPtr
, WC_TextField
);
419 normalizeRange(tPtr
, &range
);
424 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
425 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
427 tPtr
->textLen
-= range
.count
;
429 /* try to keep cursorPosition at the same place */
430 tPtr
->viewPosition
-= range
.count
;
431 if (tPtr
->viewPosition
< 0)
432 tPtr
->viewPosition
= 0;
433 tPtr
->cursorPosition
= range
.position
;
437 paintTextField(tPtr
);
443 WMGetTextFieldText(WMTextField
*tPtr
)
445 CHECK_CLASS(tPtr
, WC_TextField
);
447 return wstrdup(tPtr
->text
);
452 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
454 CHECK_CLASS(tPtr
, WC_TextField
);
456 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
457 (!text
&& tPtr
->textLen
== 0))
464 tPtr
->textLen
= strlen(text
);
466 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
467 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
468 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
470 strcpy(tPtr
->text
, text
);
473 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
474 tPtr
->viewPosition
= 0;
475 tPtr
->selection
.count
= 0;
477 if (tPtr
->view
->flags
.realized
)
478 paintTextField(tPtr
);
483 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
485 CHECK_CLASS(tPtr
, WC_TextField
);
487 tPtr
->flags
.alignment
= alignment
;
489 if (alignment
!=WALeft
) {
490 wwarning("only left alignment is supported in textfields");
494 if (tPtr
->view
->flags
.realized
) {
495 paintTextField(tPtr
);
501 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
503 CHECK_CLASS(tPtr
, WC_TextField
);
505 tPtr
->flags
.bordered
= bordered
;
507 if (tPtr
->view
->flags
.realized
) {
508 paintTextField(tPtr
);
514 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
516 CHECK_CLASS(tPtr
, WC_TextField
);
518 tPtr
->flags
.beveled
= flag
;
520 if (tPtr
->view
->flags
.realized
) {
521 paintTextField(tPtr
);
528 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
530 CHECK_CLASS(tPtr
, WC_TextField
);
532 tPtr
->flags
.secure
= flag
;
534 if (tPtr
->view
->flags
.realized
) {
535 paintTextField(tPtr
);
541 WMGetTextFieldEditable(WMTextField
*tPtr
)
543 CHECK_CLASS(tPtr
, WC_TextField
);
545 return tPtr
->flags
.enabled
;
550 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
552 CHECK_CLASS(tPtr
, WC_TextField
);
554 tPtr
->flags
.enabled
= flag
;
556 if (tPtr
->view
->flags
.realized
) {
557 paintTextField(tPtr
);
563 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
565 CHECK_CLASS(tPtr
, WC_TextField
);
567 if (tPtr
->flags
.enabled
) {
568 normalizeRange(tPtr
, &range
);
570 tPtr
->selection
= range
;
572 tPtr
->cursorPosition
= range
.position
+ range
.count
;
574 if (tPtr
->view
->flags
.realized
) {
575 paintTextField(tPtr
);
582 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
584 CHECK_CLASS(tPtr
, WC_TextField
);
586 if (tPtr
->flags
.enabled
) {
587 if (position
> tPtr
->textLen
)
588 position
= tPtr
->textLen
;
590 tPtr
->cursorPosition
= position
;
591 if (tPtr
->view
->flags
.realized
) {
592 paintTextField(tPtr
);
599 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
601 CHECK_CLASS(tPtr
, WC_TextField
);
603 if (tPtr
->view
->nextFocusChain
)
604 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
605 tPtr
->view
->nextFocusChain
= NULL
;
609 CHECK_CLASS(next
, WC_TextField
);
611 if (tPtr
->view
->nextFocusChain
)
612 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
613 if (next
->view
->prevFocusChain
)
614 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
616 tPtr
->view
->nextFocusChain
= next
->view
;
617 next
->view
->prevFocusChain
= tPtr
->view
;
622 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
624 CHECK_CLASS(tPtr
, WC_TextField
);
626 if (tPtr
->view
->prevFocusChain
)
627 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
628 tPtr
->view
->prevFocusChain
= NULL
;
632 CHECK_CLASS(prev
, WC_TextField
);
634 if (tPtr
->view
->prevFocusChain
)
635 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
636 if (prev
->view
->nextFocusChain
)
637 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
639 tPtr
->view
->prevFocusChain
= prev
->view
;
640 prev
->view
->nextFocusChain
= tPtr
->view
;
645 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
647 CHECK_CLASS(tPtr
, WC_TextField
);
650 WMReleaseFont(tPtr
->font
);
651 tPtr
->font
= WMRetainFont(font
);
654 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
656 if (tPtr
->view
->flags
.realized
) {
657 paintTextField(tPtr
);
664 WMGetTextFieldFont(WMTextField
*tPtr
)
671 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
673 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
676 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
678 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
/*+ 2*/;
683 makeHiddenString(int length
)
685 char *data
= wmalloc(length
+1);
687 memset(data
, '*', length
);
695 paintCursor(TextField
*tPtr
)
698 WMScreen
*screen
= tPtr
->view
->screen
;
702 if (tPtr
->flags
.secure
)
703 text
= makeHiddenString(strlen(tPtr
->text
));
707 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
708 tPtr
->cursorPosition
-tPtr
->viewPosition
);
710 switch (tPtr
->flags
.alignment
) {
712 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
713 if (textWidth
< tPtr
->usableWidth
)
714 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
716 cx
+= tPtr
->offsetWidth
+ 1;
719 cx
+= tPtr
->offsetWidth
+ 1;
724 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
725 if (textWidth
< tPtr
->usableWidth
)
726 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
728 cx
+= tPtr
->offsetWidth
;
732 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
733 cx, tPtr->offsetWidth, 1,
734 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
735 printf("%d %d\n",cx,tPtr->cursorPosition);
737 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
738 cx
, tPtr
->offsetWidth
, cx
,
739 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
741 if (tPtr
->flags
.secure
)
748 drawRelief(WMView
*view
, Bool beveled
)
750 WMScreen
*scr
= view
->screen
;
751 Display
*dpy
= scr
->display
;
755 int width
= view
->size
.width
;
756 int height
= view
->size
.height
;
758 dgc
= WMColorGC(scr
->darkGray
);
761 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
765 wgc
= WMColorGC(scr
->white
);
766 lgc
= WMColorGC(scr
->gray
);
769 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
770 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
772 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
773 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
776 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
777 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
779 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
780 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
785 paintTextField(TextField
*tPtr
)
787 W_Screen
*screen
= tPtr
->view
->screen
;
788 W_View
*view
= tPtr
->view
;
798 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
801 if (!tPtr
->flags
.bordered
) {
807 if (tPtr
->flags
.secure
) {
808 text
= makeHiddenString(strlen(tPtr
->text
));
813 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
815 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
816 view
->size
.width
, view
->size
.height
, screen
->depth
);
817 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
818 0,0, view
->size
.width
,view
->size
.height
);
819 /* this is quite dirty */
820 viewbuffer
.screen
= view
->screen
;
821 viewbuffer
.size
= view
->size
;
822 viewbuffer
.window
= drawbuffer
;
825 if (tPtr
->textLen
> 0) {
826 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
827 tPtr
->textLen
- tPtr
->viewPosition
);
829 th
= WMFontHeight(tPtr
->font
);
831 ty
= tPtr
->offsetWidth
;
832 switch (tPtr
->flags
.alignment
) {
834 tx
= tPtr
->offsetWidth
+ 1;
835 if (tw
< tPtr
->usableWidth
)
836 XFillRectangle(screen
->display
, drawbuffer
,
837 WMColorGC(screen
->white
),
838 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
842 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
843 if (tw
< tPtr
->usableWidth
)
844 XClearArea(screen
->display
, view
->window
, bd
, bd
,
845 totalWidth
, view
->size
.height
-2*bd
, False
);
850 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
851 if (tw
< tPtr
->usableWidth
)
852 XClearArea(screen
->display
, view
->window
, bd
, bd
,
853 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
857 if (!tPtr
->flags
.enabled
)
858 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
860 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
862 &(text
[tPtr
->viewPosition
]),
863 tPtr
->textLen
- tPtr
->viewPosition
);
865 if (tPtr
->selection
.count
) {
868 count
= tPtr
->selection
.count
< 0
869 ? tPtr
->selection
.position
+ tPtr
->selection
.count
870 : tPtr
->selection
.position
;
871 count2
= abs(tPtr
->selection
.count
);
872 if (count
< tPtr
->viewPosition
) {
873 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
874 count
= tPtr
->viewPosition
;
878 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
879 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
881 XSetBackground(screen
->display
, screen
->textFieldGC
,
882 screen
->gray
->color
.pixel
);
884 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
885 tPtr
->font
, rx
, ty
, &(text
[count
]),
888 XSetBackground(screen
->display
, screen
->textFieldGC
,
889 screen
->white
->color
.pixel
);
892 if (!tPtr
->flags
.enabled
)
893 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
895 XFillRectangle(screen
->display
, drawbuffer
,
896 WMColorGC(screen
->white
),
897 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
901 if (tPtr
->flags
.bordered
) {
902 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
905 if (tPtr
->flags
.secure
)
907 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
908 screen
->copyGC
, 0,0, view
->size
.width
,
909 view
->size
.height
,0,0);
910 XFreePixmap(screen
->display
, drawbuffer
);
913 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
921 blinkCursor(void *data
)
923 TextField
*tPtr
= (TextField
*)data
;
925 if (tPtr
->flags
.cursorOn
) {
926 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
929 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
933 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
939 handleEvents(XEvent
*event
, void *data
)
941 TextField
*tPtr
= (TextField
*)data
;
943 CHECK_CLASS(data
, WC_TextField
);
946 switch (event
->type
) {
948 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
950 tPtr
->flags
.focused
= 1;
952 if (!tPtr
->timerID
) {
953 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
957 paintTextField(tPtr
);
959 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
961 tPtr
->flags
.notIllegalMovement
= 0;
965 tPtr
->flags
.focused
= 0;
968 WMDeleteTimerHandler(tPtr
->timerID
);
969 tPtr
->timerID
= NULL
;
972 paintTextField(tPtr
);
973 if (!tPtr
->flags
.notIllegalMovement
) {
974 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
975 (void*)WMIllegalTextMovement
);
980 if (event
->xexpose
.count
!=0)
982 paintTextField(tPtr
);
986 destroyTextField(tPtr
);
993 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
997 char *textEvent
= NULL
;
999 int count
, refresh
= 0;
1000 int control_pressed
= 0;
1001 int cancelSelection
= 1;
1002 Bool shifted
, controled
, modified
;
1004 WMScreen
*scr
= tPtr
->view
->screen
;
1006 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1007 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1008 control_pressed
= 1;
1010 shifted
= event
->xkey
.state
& ShiftMask
;
1011 controled
= event
->xkey
.state
& ControlMask
;
1012 if ((event
->xkey
.state
& (ShiftMask
|ControlMask
)) != 0) {
1018 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
1019 buffer
[count
] = '\0';
1023 #ifdef XK_ISO_Left_Tab
1024 case XK_ISO_Left_Tab
:
1026 if (!controled
&& !modified
) {
1028 if (tPtr
->view
->prevFocusChain
) {
1029 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1030 tPtr
->view
->prevFocusChain
);
1031 tPtr
->flags
.notIllegalMovement
= 1;
1033 data
= (void*)WMBacktabTextMovement
;
1035 if (tPtr
->view
->nextFocusChain
) {
1036 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1037 tPtr
->view
->nextFocusChain
);
1038 tPtr
->flags
.notIllegalMovement
= 1;
1040 data
= (void*)WMTabTextMovement
;
1042 textEvent
= WMTextDidEndEditingNotification
;
1049 if (!shifted
&& !controled
&& !modified
) {
1050 data
= (void*)WMEscapeTextMovement
;
1051 textEvent
= WMTextDidEndEditingNotification
;
1058 if (!shifted
&& !controled
&& !modified
) {
1059 data
= (void*)WMReturnTextMovement
;
1060 textEvent
= WMTextDidEndEditingNotification
;
1066 case WM_EMACSKEY_LEFT
:
1067 if (!control_pressed
)
1076 if (tPtr
->cursorPosition
> 0) {
1078 if (event
->xkey
.state
& ControlMask
) {
1079 int i
= tPtr
->cursorPosition
- 1;
1081 while (i
> 0 && tPtr
->text
[i
] != ' ') i
--;
1082 while (i
> 0 && tPtr
->text
[i
] == ' ') i
--;
1084 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1086 tPtr
->cursorPosition
--;
1088 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1089 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1094 if (event
->xkey
.state
& ShiftMask
)
1095 cancelSelection
= 0;
1101 case WM_EMACSKEY_RIGHT
:
1102 if (!control_pressed
)
1112 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1114 if (event
->xkey
.state
& ControlMask
) {
1115 int i
= tPtr
->cursorPosition
;
1117 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ') i
++;
1118 while (tPtr
->text
[i
] == ' ') i
++;
1120 tPtr
->cursorPosition
= i
;
1122 tPtr
->cursorPosition
++;
1124 while (WMWidthOfString(tPtr
->font
,
1125 &(tPtr
->text
[tPtr
->viewPosition
]),
1126 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1127 > tPtr
->usableWidth
) {
1128 tPtr
->viewPosition
++;
1134 if (event
->xkey
.state
& ShiftMask
)
1135 cancelSelection
= 0;
1141 case WM_EMACSKEY_HOME
:
1142 if (!control_pressed
)
1152 if (!modified
&& !controled
) {
1153 if (tPtr
->cursorPosition
> 0) {
1155 tPtr
->cursorPosition
= 0;
1156 if (tPtr
->viewPosition
> 0) {
1157 tPtr
->viewPosition
= 0;
1162 if (event
->xkey
.state
& ShiftMask
)
1163 cancelSelection
= 0;
1169 case WM_EMACSKEY_END
:
1170 if (!control_pressed
)
1180 if (!modified
&& !controled
) {
1181 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1183 tPtr
->cursorPosition
= tPtr
->textLen
;
1184 tPtr
->viewPosition
= 0;
1185 while (WMWidthOfString(tPtr
->font
,
1186 &(tPtr
->text
[tPtr
->viewPosition
]),
1187 tPtr
->textLen
-tPtr
->viewPosition
)
1188 > tPtr
->usableWidth
) {
1189 tPtr
->viewPosition
++;
1195 if (event
->xkey
.state
& ShiftMask
)
1196 cancelSelection
= 0;
1202 case WM_EMACSKEY_BS
:
1203 if (!control_pressed
)
1211 if (!modified
&& !shifted
&& !controled
) {
1212 if (tPtr
->selection
.count
) {
1213 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1214 data
= (void*)WMDeleteTextEvent
;
1215 textEvent
= WMTextDidChangeNotification
;
1216 } else if (tPtr
->cursorPosition
> 0) {
1218 range
.position
= tPtr
->cursorPosition
- 1;
1220 WMDeleteTextFieldRange(tPtr
, range
);
1221 data
= (void*)WMDeleteTextEvent
;
1222 textEvent
= WMTextDidChangeNotification
;
1229 case WM_EMACSKEY_DEL
:
1230 if (!control_pressed
)
1241 if (!modified
&& !controled
&& !shifted
) {
1242 if (tPtr
->selection
.count
) {
1243 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1244 data
= (void*)WMDeleteTextEvent
;
1245 textEvent
= WMTextDidChangeNotification
;
1246 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1248 range
.position
= tPtr
->cursorPosition
;
1250 WMDeleteTextFieldRange(tPtr
, range
);
1251 data
= (void*)WMDeleteTextEvent
;
1252 textEvent
= WMTextDidChangeNotification
;
1262 if (count
> 0 && !iscntrl(buffer
[0])) {
1263 if (tPtr
->selection
.count
)
1264 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1265 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1266 data
= (void*)WMInsertTextEvent
;
1267 textEvent
= WMTextDidChangeNotification
;
1276 WMRelayToNextResponder(W_VIEW(tPtr
), event
);
1280 /* Do not allow text selection in secure text fields */
1281 if (cancelSelection
|| tPtr
->flags
.secure
) {
1282 lostHandler(tPtr
->view
, XA_PRIMARY
, NULL
);
1284 if (tPtr
->selection
.count
) {
1285 tPtr
->selection
.count
= 0;
1288 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1290 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1292 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1294 WMPostNotificationName("_lostOwnership", NULL
, tPtr
);
1300 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1303 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1305 if (tPtr
->delegate
) {
1306 if (textEvent
==WMTextDidBeginEditingNotification
&&
1307 tPtr
->delegate
->didBeginEditing
)
1308 (*tPtr
->delegate
->didBeginEditing
)(tPtr
->delegate
, notif
);
1310 else if (textEvent
==WMTextDidEndEditingNotification
&&
1311 tPtr
->delegate
->didEndEditing
)
1312 (*tPtr
->delegate
->didEndEditing
)(tPtr
->delegate
, notif
);
1314 else if (textEvent
==WMTextDidChangeNotification
&&
1315 tPtr
->delegate
->didChange
)
1316 (*tPtr
->delegate
->didChange
)(tPtr
->delegate
, notif
);
1319 WMPostNotification(notif
);
1320 WMReleaseNotification(notif
);
1324 paintTextField(tPtr
);
1326 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1331 pointToCursorPosition(TextField
*tPtr
, int x
)
1336 if (tPtr
->flags
.bordered
)
1339 a
= tPtr
->viewPosition
;
1340 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1341 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1342 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1343 return tPtr
->textLen
;
1345 while (a
< b
&& b
-a
>1) {
1347 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1348 mid
- tPtr
->viewPosition
);
1362 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1363 void *cdata
, WMData
*data
)
1365 TextField
*tPtr
= (TextField
*)view
->self
;
1368 tPtr
->flags
.waitingSelection
= 0;
1371 str
= (char*)WMDataBytes(data
);
1373 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1374 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1375 (void*)WMInsertTextEvent
);
1379 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1383 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1385 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1386 (void*)WMInsertTextEvent
);
1393 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1395 TextField
*tPtr
= (TextField
*)data
;
1396 static int move
= 0;
1397 static Time lastButtonReleasedEvent
= 0;
1398 Display
*dpy
= event
->xany
.display
;
1400 CHECK_CLASS(data
, WC_TextField
);
1402 switch (event
->type
) {
1404 if (tPtr
->flags
.waitingSelection
) {
1407 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1408 handleTextFieldKeyPress(tPtr
, event
);
1409 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1410 W_VIEW(tPtr
)->screen
->invisibleCursor
);
1411 tPtr
->flags
.pointerGrabbed
= 1;
1417 if (tPtr
->flags
.pointerGrabbed
) {
1418 tPtr
->flags
.pointerGrabbed
= 0;
1419 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1420 W_VIEW(tPtr
)->screen
->textCursor
);
1422 if (tPtr
->flags
.waitingSelection
) {
1426 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1428 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1429 tPtr
->usableWidth
) {
1430 if (WMWidthOfString(tPtr
->font
,
1431 &(tPtr
->text
[tPtr
->viewPosition
]),
1432 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1433 > tPtr
->usableWidth
) {
1434 tPtr
->viewPosition
++;
1436 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1438 tPtr
->viewPosition
--;
1441 tPtr
->cursorPosition
=
1442 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1444 /* Do not allow text selection in secure textfields */
1445 if (tPtr
->flags
.secure
) {
1446 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1449 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1451 if (tPtr
->selection
.count
!= 0) {
1452 if (!tPtr
->flags
.ownsSelection
) {
1453 WMCreateSelectionHandler(tPtr
->view
,
1455 event
->xbutton
.time
,
1456 &selectionHandler
, NULL
);
1457 tPtr
->flags
.ownsSelection
= 1;
1462 paintTextField(tPtr
);
1468 if (tPtr
->flags
.pointerGrabbed
) {
1469 tPtr
->flags
.pointerGrabbed
= 0;
1470 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1471 W_VIEW(tPtr
)->screen
->textCursor
);
1475 if (tPtr
->flags
.waitingSelection
) {
1480 switch (tPtr
->flags
.alignment
) {
1483 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1484 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1485 WMSetFocusToWidget(tPtr
);
1486 } else if (tPtr
->flags
.focused
) {
1487 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1488 tPtr
->selection
.count
= 0;
1490 if (textWidth
< tPtr
->usableWidth
) {
1491 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1492 event
->xbutton
.x
- tPtr
->usableWidth
1494 } else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1497 paintTextField(tPtr
);
1501 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1502 WMSetFocusToWidget(tPtr
);
1503 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1505 paintTextField(tPtr
);
1506 } else if (tPtr
->flags
.focused
1507 && event
->xbutton
.button
== Button1
) {
1508 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1510 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1511 tPtr
->selection
.count
= 0;
1512 paintTextField(tPtr
);
1514 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1518 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1519 event
->xbutton
.time
,
1521 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1525 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1527 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1528 (void*)WMInsertTextEvent
);
1531 tPtr
->flags
.waitingSelection
= 1;
1541 if (tPtr
->flags
.pointerGrabbed
) {
1542 tPtr
->flags
.pointerGrabbed
= 0;
1543 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1544 W_VIEW(tPtr
)->screen
->textCursor
);
1546 if (tPtr
->flags
.waitingSelection
) {
1550 if (tPtr
->selection
.count
!= 0) {
1552 XRotateBuffers(dpy
, 1);
1554 count
= abs(tPtr
->selection
.count
);
1555 if (tPtr
->selection
.count
< 0)
1556 start
= tPtr
->selection
.position
- count
;
1558 start
= tPtr
->selection
.position
;
1560 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1565 if (!tPtr
->flags
.secure
&&
1566 event
->xbutton
.time
- lastButtonReleasedEvent
1567 <= WINGsConfiguration
.doubleClickDelay
) {
1568 tPtr
->selection
.position
= 0;
1569 tPtr
->selection
.count
= tPtr
->textLen
;
1570 paintTextField(tPtr
);
1572 if (!tPtr
->flags
.ownsSelection
) {
1573 WMCreateSelectionHandler(tPtr
->view
,
1575 event
->xbutton
.time
,
1576 &selectionHandler
, NULL
);
1577 tPtr
->flags
.ownsSelection
= 1;
1580 WMPostNotificationName("_lostOwnership", NULL
, tPtr
);
1582 lastButtonReleasedEvent
= event
->xbutton
.time
;
1590 destroyTextField(TextField
*tPtr
)
1594 WMDeleteTimerHandler(tPtr
->timerID
);
1597 WMReleaseFont(tPtr
->font
);
1598 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1599 WMRemoveNotificationObserver(tPtr
);