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 lostSelection(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 lostSelection(WMView
*view
, Atom selection
, void *cdata
)
286 TextField
*tPtr
= (WMTextField
*)view
->self
;
288 if (tPtr
->flags
.ownsSelection
) {
289 WMDeleteSelectionHandler(view
, selection
, CurrentTime
);
290 tPtr
->flags
.ownsSelection
= 0;
292 if (tPtr
->selection
.count
!= 0) {
293 tPtr
->selection
.count
= 0;
294 paintTextField(tPtr
);
300 selectionNotification(void *observerData
, WMNotification
*notification
)
302 WMView
*observerView
= (WMView
*)observerData
;
303 WMView
*newOwnerView
= (WMView
*)WMGetNotificationClientData(notification
);
305 if (observerView
!= newOwnerView
) {
307 //if (tPtr->flags.ownsSelection)
308 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
310 lostSelection(observerView
, XA_PRIMARY
, NULL
);
316 realizeObserver(void *self
, WMNotification
*not)
318 W_CreateIC(((TextField
*)self
)->view
);
323 WMCreateTextField(WMWidget
*parent
)
327 tPtr
= wmalloc(sizeof(TextField
));
328 memset(tPtr
, 0, sizeof(TextField
));
330 tPtr
->widgetClass
= WC_TextField
;
332 tPtr
->view
= W_CreateView(W_VIEW(parent
));
337 tPtr
->view
->self
= tPtr
;
339 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
341 tPtr
->view
->attribFlags
|= CWCursor
;
342 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
344 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
346 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
349 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
351 tPtr
->flags
.enabled
= 1;
353 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
354 |FocusChangeMask
, handleEvents
, tPtr
);
356 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
358 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
359 tPtr
->flags
.beveled
= True
;
360 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
362 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
364 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
366 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
367 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
368 handleTextFieldActionEvents
, tPtr
);
370 WMAddNotificationObserver(selectionNotification
, tPtr
->view
,
371 WMSelectionOwnerDidChangeNotification
,
374 WMAddNotificationObserver(realizeObserver
, tPtr
,
375 WMViewRealizedNotification
, tPtr
->view
);
377 tPtr
->flags
.cursorOn
= 1;
384 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
386 CHECK_CLASS(tPtr
, WC_TextField
);
388 tPtr
->delegate
= delegate
;
393 WMGetTextFieldDelegate(WMTextField
*tPtr
)
395 CHECK_CLASS(tPtr
, WC_TextField
);
397 return tPtr
->delegate
;
402 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
406 CHECK_CLASS(tPtr
, WC_TextField
);
413 /* check if buffer will hold the text */
414 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
415 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
416 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
419 if (position
< 0 || position
>= tPtr
->textLen
) {
420 /* append the text at the end */
421 strcat(tPtr
->text
, text
);
425 tPtr
->textLen
+= len
;
426 tPtr
->cursorPosition
+= len
;
428 /* insert text at position */
429 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
430 tPtr
->textLen
-position
+1);
432 memcpy(&(tPtr
->text
[position
]), text
, len
);
434 tPtr
->textLen
+= len
;
435 if (position
>= tPtr
->cursorPosition
) {
436 tPtr
->cursorPosition
+= len
;
443 paintTextField(tPtr
);
447 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
449 CHECK_CLASS(tPtr
, WC_TextField
);
451 normalizeRange(tPtr
, &range
);
456 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
457 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
459 tPtr
->textLen
-= range
.count
;
461 /* try to keep cursorPosition at the same place */
462 tPtr
->viewPosition
-= range
.count
;
463 if (tPtr
->viewPosition
< 0)
464 tPtr
->viewPosition
= 0;
465 tPtr
->cursorPosition
= range
.position
;
469 paintTextField(tPtr
);
475 WMGetTextFieldText(WMTextField
*tPtr
)
477 CHECK_CLASS(tPtr
, WC_TextField
);
479 return wstrdup(tPtr
->text
);
484 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
486 CHECK_CLASS(tPtr
, WC_TextField
);
488 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
489 (!text
&& tPtr
->textLen
== 0))
496 tPtr
->textLen
= strlen(text
);
498 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
499 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
500 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
502 strcpy(tPtr
->text
, text
);
505 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
506 tPtr
->viewPosition
= 0;
507 tPtr
->selection
.count
= 0;
509 if (tPtr
->view
->flags
.realized
)
510 paintTextField(tPtr
);
515 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
517 CHECK_CLASS(tPtr
, WC_TextField
);
519 tPtr
->flags
.alignment
= alignment
;
521 if (alignment
!=WALeft
) {
522 wwarning("only left alignment is supported in textfields");
526 if (tPtr
->view
->flags
.realized
) {
527 paintTextField(tPtr
);
533 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
535 CHECK_CLASS(tPtr
, WC_TextField
);
537 tPtr
->flags
.bordered
= bordered
;
539 if (tPtr
->view
->flags
.realized
) {
540 paintTextField(tPtr
);
546 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
548 CHECK_CLASS(tPtr
, WC_TextField
);
550 tPtr
->flags
.beveled
= ((flag
==0) ? 0 : 1);
552 if (tPtr
->view
->flags
.realized
) {
553 paintTextField(tPtr
);
560 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
562 CHECK_CLASS(tPtr
, WC_TextField
);
564 tPtr
->flags
.secure
= ((flag
==0) ? 0 : 1);
566 if (tPtr
->view
->flags
.realized
) {
567 paintTextField(tPtr
);
573 WMGetTextFieldEditable(WMTextField
*tPtr
)
575 CHECK_CLASS(tPtr
, WC_TextField
);
577 return tPtr
->flags
.enabled
;
582 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
584 CHECK_CLASS(tPtr
, WC_TextField
);
586 tPtr
->flags
.enabled
= ((flag
==0) ? 0 : 1);
588 if (tPtr
->view
->flags
.realized
) {
589 paintTextField(tPtr
);
595 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
597 CHECK_CLASS(tPtr
, WC_TextField
);
599 if (tPtr
->flags
.enabled
) {
600 normalizeRange(tPtr
, &range
);
602 tPtr
->selection
= range
;
604 tPtr
->cursorPosition
= range
.position
+ range
.count
;
606 if (tPtr
->view
->flags
.realized
) {
607 paintTextField(tPtr
);
614 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
616 CHECK_CLASS(tPtr
, WC_TextField
);
618 if (tPtr
->flags
.enabled
) {
619 if (position
> tPtr
->textLen
)
620 position
= tPtr
->textLen
;
622 tPtr
->cursorPosition
= position
;
623 if (tPtr
->view
->flags
.realized
) {
624 paintTextField(tPtr
);
631 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
633 CHECK_CLASS(tPtr
, WC_TextField
);
635 if (tPtr
->view
->nextFocusChain
)
636 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
637 tPtr
->view
->nextFocusChain
= NULL
;
641 CHECK_CLASS(next
, WC_TextField
);
643 if (tPtr
->view
->nextFocusChain
)
644 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
645 if (next
->view
->prevFocusChain
)
646 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
648 tPtr
->view
->nextFocusChain
= next
->view
;
649 next
->view
->prevFocusChain
= tPtr
->view
;
654 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
656 CHECK_CLASS(tPtr
, WC_TextField
);
658 if (tPtr
->view
->prevFocusChain
)
659 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
660 tPtr
->view
->prevFocusChain
= NULL
;
664 CHECK_CLASS(prev
, WC_TextField
);
666 if (tPtr
->view
->prevFocusChain
)
667 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
668 if (prev
->view
->nextFocusChain
)
669 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
671 tPtr
->view
->prevFocusChain
= prev
->view
;
672 prev
->view
->nextFocusChain
= tPtr
->view
;
677 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
679 CHECK_CLASS(tPtr
, WC_TextField
);
682 WMReleaseFont(tPtr
->font
);
683 tPtr
->font
= WMRetainFont(font
);
686 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
688 if (tPtr
->view
->flags
.realized
) {
689 paintTextField(tPtr
);
696 WMGetTextFieldFont(WMTextField
*tPtr
)
703 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
705 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
708 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
710 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
/*+ 2*/;
715 makeHiddenString(int length
)
717 char *data
= wmalloc(length
+1);
719 memset(data
, '*', length
);
727 paintCursor(TextField
*tPtr
)
730 WMScreen
*screen
= tPtr
->view
->screen
;
734 if (tPtr
->flags
.secure
)
735 text
= makeHiddenString(strlen(tPtr
->text
));
739 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
740 tPtr
->cursorPosition
-tPtr
->viewPosition
);
742 switch (tPtr
->flags
.alignment
) {
744 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
745 if (textWidth
< tPtr
->usableWidth
)
746 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
748 cx
+= tPtr
->offsetWidth
+ 1;
751 cx
+= tPtr
->offsetWidth
+ 1;
756 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
757 if (textWidth
< tPtr
->usableWidth
)
758 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
760 cx
+= tPtr
->offsetWidth
;
764 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
765 cx, tPtr->offsetWidth, 1,
766 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
767 printf("%d %d\n",cx,tPtr->cursorPosition);
770 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
771 cx
, tPtr
->offsetWidth
, cx
,
772 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
774 W_SetPreeditPositon(tPtr
->view
, cx
, 0);
776 if (tPtr
->flags
.secure
) {
784 drawRelief(WMView
*view
, Bool beveled
)
786 WMScreen
*scr
= view
->screen
;
787 Display
*dpy
= scr
->display
;
791 int width
= view
->size
.width
;
792 int height
= view
->size
.height
;
794 dgc
= WMColorGC(scr
->darkGray
);
797 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
801 wgc
= WMColorGC(scr
->white
);
802 lgc
= WMColorGC(scr
->gray
);
805 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
806 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
808 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
809 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
812 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
813 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
815 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
816 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
821 paintTextField(TextField
*tPtr
)
823 W_Screen
*screen
= tPtr
->view
->screen
;
824 W_View
*view
= tPtr
->view
;
835 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
838 if (!tPtr
->flags
.bordered
) {
844 if (tPtr
->flags
.secure
) {
845 text
= makeHiddenString(strlen(tPtr
->text
));
850 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
852 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
853 view
->size
.width
, view
->size
.height
, screen
->depth
);
854 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
855 0,0, view
->size
.width
,view
->size
.height
);
856 /* this is quite dirty */
857 viewbuffer
.screen
= view
->screen
;
858 viewbuffer
.size
= view
->size
;
859 viewbuffer
.window
= drawbuffer
;
862 if (tPtr
->textLen
> 0) {
863 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
864 tPtr
->textLen
- tPtr
->viewPosition
);
866 th
= WMFontHeight(tPtr
->font
);
868 ty
= tPtr
->offsetWidth
;
869 switch (tPtr
->flags
.alignment
) {
871 tx
= tPtr
->offsetWidth
+ 1;
872 if (tw
< tPtr
->usableWidth
)
873 XFillRectangle(screen
->display
, drawbuffer
,
874 WMColorGC(screen
->white
),
875 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
879 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
880 if (tw
< tPtr
->usableWidth
)
881 XClearArea(screen
->display
, view
->window
, bd
, bd
,
882 totalWidth
, view
->size
.height
-2*bd
, False
);
887 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
888 if (tw
< tPtr
->usableWidth
)
889 XClearArea(screen
->display
, view
->window
, bd
, bd
,
890 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
894 color
= tPtr
->flags
.enabled
? screen
->black
: screen
->darkGray
;
896 WMDrawImageString(screen
, drawbuffer
, color
, screen
->white
,
897 tPtr
->font
, tx
, ty
, &(text
[tPtr
->viewPosition
]),
898 tPtr
->textLen
- tPtr
->viewPosition
);
900 if (tPtr
->selection
.count
) {
903 count
= tPtr
->selection
.count
< 0
904 ? tPtr
->selection
.position
+ tPtr
->selection
.count
905 : tPtr
->selection
.position
;
906 count2
= abs(tPtr
->selection
.count
);
907 if (count
< tPtr
->viewPosition
) {
908 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
909 count
= tPtr
->viewPosition
;
912 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
913 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
915 WMDrawImageString(screen
, drawbuffer
, color
, screen
->gray
,
916 tPtr
->font
, rx
, ty
, &(text
[count
]), count2
);
919 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
920 bd
, bd
, totalWidth
,view
->size
.height
-2*bd
);
924 if (tPtr
->flags
.bordered
) {
925 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
928 if (tPtr
->flags
.secure
)
930 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
931 screen
->copyGC
, 0,0, view
->size
.width
,
932 view
->size
.height
,0,0);
933 XFreePixmap(screen
->display
, drawbuffer
);
936 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
944 blinkCursor(void *data
)
946 TextField
*tPtr
= (TextField
*)data
;
948 if (tPtr
->flags
.cursorOn
) {
949 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
952 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
956 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
962 handleEvents(XEvent
*event
, void *data
)
964 TextField
*tPtr
= (TextField
*)data
;
966 CHECK_CLASS(data
, WC_TextField
);
968 switch (event
->type
) {
970 W_FocusIC(tPtr
->view
);
971 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
973 tPtr
->flags
.focused
= 1;
975 if (!tPtr
->timerID
) {
976 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
980 paintTextField(tPtr
);
982 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
984 tPtr
->flags
.notIllegalMovement
= 0;
988 W_UnFocusIC(tPtr
->view
);
989 tPtr
->flags
.focused
= 0;
992 WMDeleteTimerHandler(tPtr
->timerID
);
993 tPtr
->timerID
= NULL
;
996 paintTextField(tPtr
);
997 if (!tPtr
->flags
.notIllegalMovement
) {
998 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
999 (void*)WMIllegalTextMovement
);
1004 if (event
->xexpose
.count
!=0)
1006 paintTextField(tPtr
);
1010 destroyTextField(tPtr
);
1017 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
1021 char *textEvent
= NULL
;
1023 int count
, refresh
= 0;
1024 int control_pressed
= 0;
1025 int cancelSelection
= 1;
1026 Bool shifted
, controled
, modified
;
1029 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1030 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
1031 control_pressed
= 1;
1033 shifted
= (event
->xkey
.state
& ShiftMask
? True
: False
);
1034 controled
= (event
->xkey
.state
& ControlMask
? True
: False
);
1035 modified
= shifted
|| controled
;
1037 count
= W_LookupString(tPtr
->view
, &event
->xkey
, buffer
, 63, &ksym
, NULL
);
1038 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
1039 buffer
[count
] = '\0';
1043 #ifdef XK_ISO_Left_Tab
1044 case XK_ISO_Left_Tab
:
1048 if (tPtr
->view
->prevFocusChain
) {
1049 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1050 tPtr
->view
->prevFocusChain
);
1051 tPtr
->flags
.notIllegalMovement
= 1;
1053 data
= (void*)WMBacktabTextMovement
;
1055 if (tPtr
->view
->nextFocusChain
) {
1056 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1057 tPtr
->view
->nextFocusChain
);
1058 tPtr
->flags
.notIllegalMovement
= 1;
1060 data
= (void*)WMTabTextMovement
;
1062 textEvent
= WMTextDidEndEditingNotification
;
1064 cancelSelection
= 0;
1072 data
= (void*)WMEscapeTextMovement
;
1073 textEvent
= WMTextDidEndEditingNotification
;
1081 data
= (void*)WMReturnTextMovement
;
1082 textEvent
= WMTextDidEndEditingNotification
;
1088 case WM_EMACSKEY_LEFT
:
1089 if (!control_pressed
)
1098 if (tPtr
->cursorPosition
> 0) {
1101 int i
= tPtr
->cursorPosition
- 1;
1103 while (i
> 0 && tPtr
->text
[i
] != ' ') i
--;
1104 while (i
> 0 && tPtr
->text
[i
] == ' ') i
--;
1106 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1108 tPtr
->cursorPosition
--;
1110 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1111 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1117 cancelSelection
= 0;
1123 case WM_EMACSKEY_RIGHT
:
1124 if (!control_pressed
)
1133 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1136 int i
= tPtr
->cursorPosition
;
1138 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ') i
++;
1139 while (tPtr
->text
[i
] == ' ') i
++;
1141 tPtr
->cursorPosition
= i
;
1143 tPtr
->cursorPosition
++;
1145 while (WMWidthOfString(tPtr
->font
,
1146 &(tPtr
->text
[tPtr
->viewPosition
]),
1147 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1148 > tPtr
->usableWidth
) {
1149 tPtr
->viewPosition
++;
1156 cancelSelection
= 0;
1162 case WM_EMACSKEY_HOME
:
1163 if (!control_pressed
)
1173 if (tPtr
->cursorPosition
> 0) {
1175 tPtr
->cursorPosition
= 0;
1176 if (tPtr
->viewPosition
> 0) {
1177 tPtr
->viewPosition
= 0;
1183 cancelSelection
= 0;
1189 case WM_EMACSKEY_END
:
1190 if (!control_pressed
)
1200 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1202 tPtr
->cursorPosition
= tPtr
->textLen
;
1203 tPtr
->viewPosition
= 0;
1204 while (WMWidthOfString(tPtr
->font
,
1205 &(tPtr
->text
[tPtr
->viewPosition
]),
1206 tPtr
->textLen
-tPtr
->viewPosition
)
1207 > tPtr
->usableWidth
) {
1208 tPtr
->viewPosition
++;
1215 cancelSelection
= 0;
1221 case WM_EMACSKEY_BS
:
1222 if (!control_pressed
)
1229 if (tPtr
->selection
.count
) {
1230 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1231 data
= (void*)WMDeleteTextEvent
;
1232 textEvent
= WMTextDidChangeNotification
;
1233 } else if (tPtr
->cursorPosition
> 0) {
1235 range
.position
= tPtr
->cursorPosition
- 1;
1237 WMDeleteTextFieldRange(tPtr
, range
);
1238 data
= (void*)WMDeleteTextEvent
;
1239 textEvent
= WMTextDidChangeNotification
;
1246 case WM_EMACSKEY_DEL
:
1247 if (!control_pressed
)
1257 if (tPtr
->selection
.count
) {
1258 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1259 data
= (void*)WMDeleteTextEvent
;
1260 textEvent
= WMTextDidChangeNotification
;
1261 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1263 range
.position
= tPtr
->cursorPosition
;
1265 WMDeleteTextFieldRange(tPtr
, range
);
1266 data
= (void*)WMDeleteTextEvent
;
1267 textEvent
= WMTextDidChangeNotification
;
1277 if (count
> 0 && !iscntrl(buffer
[0])) {
1278 if (tPtr
->selection
.count
)
1279 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1280 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1281 data
= (void*)WMInsertTextEvent
;
1282 textEvent
= WMTextDidChangeNotification
;
1291 WMRelayToNextResponder(W_VIEW(tPtr
), event
);
1295 /* Do not allow text selection in secure text fields */
1296 if (cancelSelection
|| tPtr
->flags
.secure
) {
1297 lostSelection(tPtr
->view
, XA_PRIMARY
, NULL
);
1299 if (tPtr
->selection
.count
) {
1300 tPtr
->selection
.count
= 0;
1303 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1305 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1307 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1313 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1316 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1318 if (tPtr
->delegate
) {
1319 if (textEvent
==WMTextDidBeginEditingNotification
&&
1320 tPtr
->delegate
->didBeginEditing
)
1321 (*tPtr
->delegate
->didBeginEditing
)(tPtr
->delegate
, notif
);
1323 else if (textEvent
==WMTextDidEndEditingNotification
&&
1324 tPtr
->delegate
->didEndEditing
)
1325 (*tPtr
->delegate
->didEndEditing
)(tPtr
->delegate
, notif
);
1327 else if (textEvent
==WMTextDidChangeNotification
&&
1328 tPtr
->delegate
->didChange
)
1329 (*tPtr
->delegate
->didChange
)(tPtr
->delegate
, notif
);
1332 WMPostNotification(notif
);
1333 WMReleaseNotification(notif
);
1337 paintTextField(tPtr
);
1339 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1344 pointToCursorPosition(TextField
*tPtr
, int x
)
1349 if (tPtr
->flags
.bordered
)
1352 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1353 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1354 return tPtr
->textLen
;
1356 a
= tPtr
->viewPosition
;
1359 while (a
< b
&& b
-a
>1) {
1361 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1362 mid
- tPtr
->viewPosition
);
1377 pasteText(WMView
*view
, Atom selection
, Atom target
, Time timestamp
,
1378 void *cdata
, WMData
*data
)
1380 TextField
*tPtr
= (TextField
*)view
->self
;
1383 tPtr
->flags
.waitingSelection
= 0;
1386 str
= (char*)WMDataBytes(data
);
1388 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1389 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1390 (void*)WMInsertTextEvent
);
1394 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1398 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1400 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1401 (void*)WMInsertTextEvent
);
1408 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1410 TextField
*tPtr
= (TextField
*)data
;
1411 static int move
= 0;
1412 static Time lastButtonReleasedEvent
= 0;
1413 static Time lastButtonReleasedEvent2
= 0;
1414 Display
*dpy
= event
->xany
.display
;
1416 CHECK_CLASS(data
, WC_TextField
);
1418 switch (event
->type
) {
1420 if (tPtr
->flags
.waitingSelection
) {
1423 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1424 handleTextFieldKeyPress(tPtr
, event
);
1425 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1426 W_VIEW(tPtr
)->screen
->invisibleCursor
);
1427 tPtr
->flags
.pointerGrabbed
= 1;
1433 if (tPtr
->flags
.pointerGrabbed
) {
1434 tPtr
->flags
.pointerGrabbed
= 0;
1435 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1436 W_VIEW(tPtr
)->screen
->textCursor
);
1438 if (tPtr
->flags
.waitingSelection
) {
1442 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1444 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1445 tPtr
->usableWidth
) {
1446 if (WMWidthOfString(tPtr
->font
,
1447 &(tPtr
->text
[tPtr
->viewPosition
]),
1448 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1449 > tPtr
->usableWidth
) {
1450 tPtr
->viewPosition
++;
1452 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1454 tPtr
->viewPosition
--;
1457 tPtr
->cursorPosition
=
1458 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1460 /* Do not allow text selection in secure textfields */
1461 if (tPtr
->flags
.secure
) {
1462 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1465 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1468 paintTextField(tPtr
);
1474 if (tPtr
->flags
.pointerGrabbed
) {
1475 tPtr
->flags
.pointerGrabbed
= 0;
1476 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1477 W_VIEW(tPtr
)->screen
->textCursor
);
1481 if (tPtr
->flags
.waitingSelection
) {
1486 switch (tPtr
->flags
.alignment
) {
1489 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1490 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1491 WMSetFocusToWidget(tPtr
);
1493 if (tPtr
->flags
.focused
) {
1494 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1495 tPtr
->selection
.count
= 0;
1497 if (textWidth
< tPtr
->usableWidth
) {
1498 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1499 event
->xbutton
.x
- tPtr
->usableWidth
1501 } else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1504 paintTextField(tPtr
);
1508 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1509 WMSetFocusToWidget(tPtr
);
1511 if (tPtr
->flags
.focused
&& event
->xbutton
.button
== Button1
) {
1512 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1514 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1515 tPtr
->selection
.count
= 0;
1516 paintTextField(tPtr
);
1518 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1522 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1523 event
->xbutton
.time
,
1525 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1529 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1531 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1532 (void*)WMInsertTextEvent
);
1535 tPtr
->flags
.waitingSelection
= 1;
1545 if (tPtr
->flags
.pointerGrabbed
) {
1546 tPtr
->flags
.pointerGrabbed
= 0;
1547 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
,
1548 W_VIEW(tPtr
)->screen
->textCursor
);
1550 if (tPtr
->flags
.waitingSelection
) {
1554 if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!=0) {
1556 XRotateBuffers(dpy
, 1);
1558 count
= abs(tPtr
->selection
.count
);
1559 if (tPtr
->selection
.count
< 0)
1560 start
= tPtr
->selection
.position
- count
;
1562 start
= tPtr
->selection
.position
;
1564 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1569 if (!tPtr
->flags
.secure
&&
1570 event
->xbutton
.time
- lastButtonReleasedEvent
1571 <= WINGsConfiguration
.doubleClickDelay
) {
1573 if (event
->xbutton
.time
- lastButtonReleasedEvent2
<= 2*WINGsConfiguration
.doubleClickDelay
) {
1574 tPtr
->selection
.position
= 0;
1575 tPtr
->selection
.count
= tPtr
->textLen
;
1579 pos
= tPtr
->selection
.position
;
1580 cnt
= tPtr
->selection
.count
;
1583 if (txt
[pos
] == ' ' || txt
[pos
] == '\t') break;
1588 while(pos
+ cnt
< tPtr
->textLen
) {
1589 if (txt
[pos
+ cnt
] == ' ' || txt
[pos
+ cnt
] == '\t')
1593 tPtr
->selection
.position
= pos
;
1594 tPtr
->selection
.count
= cnt
;
1596 paintTextField(tPtr
);
1598 if (!tPtr
->flags
.ownsSelection
) {
1599 tPtr
->flags
.ownsSelection
=
1600 WMCreateSelectionHandler(tPtr
->view
,
1602 event
->xbutton
.time
,
1603 &selectionHandler
, NULL
);
1605 } else if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!=0 &&
1606 !tPtr
->flags
.ownsSelection
) {
1607 tPtr
->flags
.ownsSelection
=
1608 WMCreateSelectionHandler(tPtr
->view
,
1610 event
->xbutton
.time
,
1611 &selectionHandler
, NULL
);
1614 lastButtonReleasedEvent2
= lastButtonReleasedEvent
;
1615 lastButtonReleasedEvent
= event
->xbutton
.time
;
1623 destroyTextField(TextField
*tPtr
)
1627 WMDeleteTimerHandler(tPtr
->timerID
);
1630 W_DestroyIC(tPtr
->view
);
1632 WMReleaseFont(tPtr
->font
);
1633 /*// use lostSelection() instead of WMDeleteSelectionHandler here?*/
1634 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1635 WMRemoveNotificationObserver(tPtr
);