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;
69 unsigned int notIllegalMovement
:1;
74 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
75 if ((T)->delegate && (T)->delegate->C)\
76 (*(T)->delegate->C)((T)->delegate,notif);\
77 WMPostNotification(notif);\
78 WMReleaseNotification(notif);}
81 #define MIN_TEXT_BUFFER 2
82 #define TEXT_BUFFER_INCR 8
85 #define WM_EMACSKEYMASK ControlMask
87 #define WM_EMACSKEY_LEFT XK_b
88 #define WM_EMACSKEY_RIGHT XK_f
89 #define WM_EMACSKEY_HOME XK_a
90 #define WM_EMACSKEY_END XK_e
91 #define WM_EMACSKEY_BS XK_h
92 #define WM_EMACSKEY_DEL XK_d
96 #define DEFAULT_WIDTH 60
97 #define DEFAULT_HEIGHT 20
98 #define DEFAULT_BORDERED True
99 #define DEFAULT_ALIGNMENT WALeft
103 static void destroyTextField(TextField
*tPtr
);
104 static void paintTextField(TextField
*tPtr
);
106 static void handleEvents(XEvent
*event
, void *data
);
107 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
108 static void didResizeTextField();
110 struct W_ViewDelegate _TextFieldViewDelegate
= {
119 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
120 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
122 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
123 &((tPtr)->text[(start)]), (end) - (start) + 1))
127 normalizeRange(TextField
*tPtr
, WMRange
*range
)
129 if (range
->position
< 0 && range
->count
< 0)
132 if (range
->count
== 0) {
137 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
138 if (range
->count
< 0) { /* && range->position >= 0 */
139 if (range
->position
+ range
->count
< 0) {
140 range
->count
= range
->position
;
143 range
->count
= -range
->count
;
144 range
->position
-= range
->count
;
146 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
147 } else if (range
->position
< 0) { /* && range->count > 0 */
148 if (range
->position
+ range
->count
< 0) {
149 range
->position
= range
->count
= 0;
151 range
->count
+= range
->position
;
156 if (range
->position
+ range
->count
> tPtr
->textLen
)
157 range
->count
= tPtr
->textLen
- range
->position
;
161 memmv(char *dest
, char *src
, int size
)
166 for (i
=size
-1; i
>=0; i
--) {
169 } else if (dest
< src
) {
170 for (i
=0; i
<size
; i
++) {
178 incrToFit(TextField
*tPtr
)
180 int vp
= tPtr
->viewPosition
;
182 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
183 tPtr
->viewPosition
++;
185 return vp
!=tPtr
->viewPosition
;
190 incrToFit2(TextField
*tPtr
)
192 int vp
= tPtr
->viewPosition
;
193 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
194 >= tPtr
->usableWidth
)
195 tPtr
->viewPosition
++;
198 return vp
!=tPtr
->viewPosition
;
203 decrToFit(TextField
*tPtr
)
205 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
206 && tPtr
->viewPosition
>0)
207 tPtr
->viewPosition
--;
214 requestHandler(WMWidget
*w
, Atom selection
, Atom target
, Atom
*type
,
215 void **value
, unsigned *length
, int *format
)
219 Display
*dpy
= tPtr
->view
->screen
->display
;
222 text
= XGetAtomName(tPtr
->view
->screen
->display
,target
);
224 text
= XGetAtomName(tPtr
->view
->screen
->display
,selection
);
230 count
= tPtr
->selection
.count
< 0
231 ? tPtr
->selection
.position
+ tPtr
->selection
.count
232 : tPtr
->selection
.position
;
234 if (target
== XA_STRING
||
235 target
== XInternAtom(dpy
, "TEXT", False
) ||
236 target
== XInternAtom(dpy
, "COMPOUND_TEXT", False
)) {
237 *value
= wstrdup(&(tPtr
->text
[count
]));
238 *length
= abs(tPtr
->selection
.count
);
244 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
245 if (target
== _TARGETS
) {
249 ptr
= *value
= (char *) wmalloc(4 * sizeof(Atom
));
252 ptr
[2] = XInternAtom(dpy
, "TEXT", False
);
253 ptr
[3] = XInternAtom(dpy
, "COMPOUND_TEXT", False
);
258 *target = XA_PRIMARY;
266 lostHandler(WMWidget
*w
, Atom selection
)
268 TextField
*tPtr
= (WMTextField
*)w
;
270 tPtr
->selection
.count
= 0;
271 paintTextField(tPtr
);
275 _notification(void *observerData
, WMNotification
*notification
)
277 WMTextField
*to
= (WMTextField
*)observerData
;
278 WMTextField
*tw
= (WMTextField
*)WMGetNotificationClientData(notification
);
279 if (to
!= tw
) lostHandler(to
, 0);
283 WMCreateTextField(WMWidget
*parent
)
287 tPtr
= wmalloc(sizeof(TextField
));
288 memset(tPtr
, 0, sizeof(TextField
));
290 tPtr
->widgetClass
= WC_TextField
;
292 tPtr
->view
= W_CreateView(W_VIEW(parent
));
297 tPtr
->view
->self
= tPtr
;
299 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
301 tPtr
->view
->attribFlags
|= CWCursor
;
302 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
304 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
306 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
309 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
311 tPtr
->flags
.enabled
= 1;
313 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
314 |FocusChangeMask
, handleEvents
, tPtr
);
316 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
318 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
319 tPtr
->flags
.beveled
= True
;
320 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
322 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
324 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
326 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
327 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
328 handleTextFieldActionEvents
, tPtr
);
330 WMCreateSelectionHandler(tPtr
, XA_PRIMARY
, CurrentTime
, requestHandler
,
332 WMAddNotificationObserver(_notification
, tPtr
, "_lostOwnership", tPtr
);
335 tPtr
->flags
.cursorOn
= 1;
342 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
344 CHECK_CLASS(tPtr
, WC_TextField
);
346 tPtr
->delegate
= delegate
;
351 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
355 CHECK_CLASS(tPtr
, WC_TextField
);
362 /* check if buffer will hold the text */
363 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
364 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
365 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
368 if (position
< 0 || position
>= tPtr
->textLen
) {
369 /* append the text at the end */
370 strcat(tPtr
->text
, text
);
374 tPtr
->textLen
+= len
;
375 tPtr
->cursorPosition
+= len
;
377 /* insert text at position */
378 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
379 tPtr
->textLen
-position
+1);
381 memcpy(&(tPtr
->text
[position
]), text
, len
);
383 tPtr
->textLen
+= len
;
384 if (position
>= tPtr
->cursorPosition
) {
385 tPtr
->cursorPosition
+= len
;
392 paintTextField(tPtr
);
396 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
398 CHECK_CLASS(tPtr
, WC_TextField
);
400 normalizeRange(tPtr
, &range
);
405 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
406 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
408 tPtr
->textLen
-= range
.count
;
410 /* try to keep cursorPosition at the same place */
411 tPtr
->viewPosition
-= range
.count
;
412 if (tPtr
->viewPosition
< 0)
413 tPtr
->viewPosition
= 0;
414 tPtr
->cursorPosition
= range
.position
;
418 paintTextField(tPtr
);
424 WMGetTextFieldText(WMTextField
*tPtr
)
426 CHECK_CLASS(tPtr
, WC_TextField
);
428 return wstrdup(tPtr
->text
);
433 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
435 CHECK_CLASS(tPtr
, WC_TextField
);
437 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
438 (!text
&& tPtr
->textLen
== 0))
445 tPtr
->textLen
= strlen(text
);
447 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
448 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
449 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
451 strcpy(tPtr
->text
, text
);
454 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
455 tPtr
->viewPosition
= 0;
456 tPtr
->selection
.count
= 0;
458 if (tPtr
->view
->flags
.realized
)
459 paintTextField(tPtr
);
464 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
466 CHECK_CLASS(tPtr
, WC_TextField
);
468 tPtr
->flags
.alignment
= alignment
;
470 if (alignment
!=WALeft
) {
471 wwarning("only left alignment is supported in textfields");
475 if (tPtr
->view
->flags
.realized
) {
476 paintTextField(tPtr
);
482 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
484 CHECK_CLASS(tPtr
, WC_TextField
);
486 tPtr
->flags
.bordered
= bordered
;
488 if (tPtr
->view
->flags
.realized
) {
489 paintTextField(tPtr
);
495 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
497 CHECK_CLASS(tPtr
, WC_TextField
);
499 tPtr
->flags
.beveled
= flag
;
501 if (tPtr
->view
->flags
.realized
) {
502 paintTextField(tPtr
);
509 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
511 CHECK_CLASS(tPtr
, WC_TextField
);
513 tPtr
->flags
.secure
= flag
;
515 if (tPtr
->view
->flags
.realized
) {
516 paintTextField(tPtr
);
522 WMGetTextFieldEditable(WMTextField
*tPtr
)
524 CHECK_CLASS(tPtr
, WC_TextField
);
526 return tPtr
->flags
.enabled
;
531 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
533 CHECK_CLASS(tPtr
, WC_TextField
);
535 tPtr
->flags
.enabled
= flag
;
537 if (tPtr
->view
->flags
.realized
) {
538 paintTextField(tPtr
);
544 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
546 CHECK_CLASS(tPtr
, WC_TextField
);
548 if (tPtr
->flags
.enabled
) {
549 normalizeRange(tPtr
, &range
);
551 tPtr
->selection
= range
;
553 if (tPtr
->view
->flags
.realized
) {
554 paintTextField(tPtr
);
561 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
563 CHECK_CLASS(tPtr
, WC_TextField
);
565 if (tPtr
->flags
.enabled
) {
566 if (position
> tPtr
->textLen
)
567 position
= tPtr
->textLen
;
569 tPtr
->cursorPosition
= position
;
570 if (tPtr
->view
->flags
.realized
) {
571 paintTextField(tPtr
);
578 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
580 CHECK_CLASS(tPtr
, WC_TextField
);
582 if (tPtr
->view
->nextFocusChain
)
583 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
584 tPtr
->view
->nextFocusChain
= NULL
;
588 CHECK_CLASS(next
, WC_TextField
);
590 if (tPtr
->view
->nextFocusChain
)
591 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
592 if (next
->view
->prevFocusChain
)
593 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
595 tPtr
->view
->nextFocusChain
= next
->view
;
596 next
->view
->prevFocusChain
= tPtr
->view
;
601 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
603 CHECK_CLASS(tPtr
, WC_TextField
);
605 if (tPtr
->view
->prevFocusChain
)
606 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
607 tPtr
->view
->prevFocusChain
= NULL
;
611 CHECK_CLASS(prev
, WC_TextField
);
613 if (tPtr
->view
->prevFocusChain
)
614 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
615 if (prev
->view
->nextFocusChain
)
616 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
618 tPtr
->view
->prevFocusChain
= prev
->view
;
619 prev
->view
->nextFocusChain
= tPtr
->view
;
624 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
626 CHECK_CLASS(tPtr
, WC_TextField
);
629 WMReleaseFont(tPtr
->font
);
630 tPtr
->font
= WMRetainFont(font
);
633 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
635 if (tPtr
->view
->flags
.realized
) {
636 paintTextField(tPtr
);
643 WMGetTextFieldFont(WMTextField
*tPtr
)
650 didResizeTextField(W_ViewDelegate
*self
, WMView
*view
)
652 WMTextField
*tPtr
= (WMTextField
*)view
->self
;
655 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
657 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
662 makeHiddenString(int length
)
664 char *data
= wmalloc(length
+1);
666 memset(data
, '*', length
);
674 paintCursor(TextField
*tPtr
)
677 WMScreen
*screen
= tPtr
->view
->screen
;
681 if (tPtr
->flags
.secure
)
682 text
= makeHiddenString(strlen(tPtr
->text
));
686 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
687 tPtr
->cursorPosition
-tPtr
->viewPosition
);
689 switch (tPtr
->flags
.alignment
) {
691 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
692 if (textWidth
< tPtr
->usableWidth
)
693 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
695 cx
+= tPtr
->offsetWidth
+ 1;
698 cx
+= tPtr
->offsetWidth
+ 1;
703 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
704 if (textWidth
< tPtr
->usableWidth
)
705 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
707 cx
+= tPtr
->offsetWidth
;
711 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
712 cx, tPtr->offsetWidth, 1,
713 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
714 printf("%d %d\n",cx,tPtr->cursorPosition);
716 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
717 cx
, tPtr
->offsetWidth
, cx
,
718 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
720 if (tPtr
->flags
.secure
)
727 drawRelief(WMView
*view
, Bool beveled
)
729 WMScreen
*scr
= view
->screen
;
730 Display
*dpy
= scr
->display
;
734 int width
= view
->size
.width
;
735 int height
= view
->size
.height
;
737 dgc
= WMColorGC(scr
->darkGray
);
740 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
744 wgc
= WMColorGC(scr
->white
);
745 lgc
= WMColorGC(scr
->gray
);
748 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
749 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
751 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
752 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
755 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
756 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
758 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
759 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
764 paintTextField(TextField
*tPtr
)
766 W_Screen
*screen
= tPtr
->view
->screen
;
767 W_View
*view
= tPtr
->view
;
777 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
780 if (!tPtr
->flags
.bordered
) {
786 if (tPtr
->flags
.secure
) {
787 text
= makeHiddenString(strlen(tPtr
->text
));
792 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
794 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
795 view
->size
.width
, view
->size
.height
, screen
->depth
);
796 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
797 0,0, view
->size
.width
,view
->size
.height
);
798 /* this is quite dirty */
799 viewbuffer
.screen
= view
->screen
;
800 viewbuffer
.size
= view
->size
;
801 viewbuffer
.window
= drawbuffer
;
804 if (tPtr
->textLen
> 0) {
805 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
806 tPtr
->textLen
- tPtr
->viewPosition
);
808 th
= WMFontHeight(tPtr
->font
);
810 ty
= tPtr
->offsetWidth
;
811 switch (tPtr
->flags
.alignment
) {
813 tx
= tPtr
->offsetWidth
+ 1;
814 if (tw
< tPtr
->usableWidth
)
815 XFillRectangle(screen
->display
, drawbuffer
,
816 WMColorGC(screen
->white
),
817 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
821 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
822 if (tw
< tPtr
->usableWidth
)
823 XClearArea(screen
->display
, view
->window
, bd
, bd
,
824 totalWidth
, view
->size
.height
-2*bd
, False
);
829 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
830 if (tw
< tPtr
->usableWidth
)
831 XClearArea(screen
->display
, view
->window
, bd
, bd
,
832 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
836 if (!tPtr
->flags
.enabled
)
837 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
839 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
841 &(text
[tPtr
->viewPosition
]),
842 tPtr
->textLen
- tPtr
->viewPosition
);
844 if (tPtr
->selection
.count
) {
847 count
= tPtr
->selection
.count
< 0
848 ? tPtr
->selection
.position
+ tPtr
->selection
.count
849 : tPtr
->selection
.position
;
850 count2
= abs(tPtr
->selection
.count
);
851 if (count
< tPtr
->viewPosition
) {
852 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
853 count
= tPtr
->viewPosition
;
857 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
858 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
860 XSetBackground(screen
->display
, screen
->textFieldGC
,
861 screen
->gray
->color
.pixel
);
863 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
864 tPtr
->font
, rx
, ty
, &(text
[count
]),
867 XSetBackground(screen
->display
, screen
->textFieldGC
,
868 screen
->white
->color
.pixel
);
871 if (!tPtr
->flags
.enabled
)
872 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
874 XFillRectangle(screen
->display
, drawbuffer
,
875 WMColorGC(screen
->white
),
876 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
880 if (tPtr
->flags
.bordered
) {
881 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
884 if (tPtr
->flags
.secure
)
886 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
887 screen
->copyGC
, 0,0, view
->size
.width
,
888 view
->size
.height
,0,0);
889 XFreePixmap(screen
->display
, drawbuffer
);
892 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
900 blinkCursor(void *data
)
902 TextField
*tPtr
= (TextField
*)data
;
904 if (tPtr
->flags
.cursorOn
) {
905 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
908 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
912 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
918 handleEvents(XEvent
*event
, void *data
)
920 TextField
*tPtr
= (TextField
*)data
;
922 CHECK_CLASS(data
, WC_TextField
);
925 switch (event
->type
) {
927 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
929 tPtr
->flags
.focused
= 1;
931 if (!tPtr
->timerID
) {
932 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
936 paintTextField(tPtr
);
938 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
940 tPtr
->flags
.notIllegalMovement
= 0;
944 tPtr
->flags
.focused
= 0;
947 WMDeleteTimerHandler(tPtr
->timerID
);
948 tPtr
->timerID
= NULL
;
951 paintTextField(tPtr
);
952 if (!tPtr
->flags
.notIllegalMovement
) {
953 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
954 (void*)WMIllegalTextMovement
);
959 if (event
->xexpose
.count
!=0)
961 paintTextField(tPtr
);
965 destroyTextField(tPtr
);
972 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
976 int count
, refresh
= 0;
977 int control_pressed
= 0;
978 int cancelSelection
= 1;
980 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
981 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
984 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
985 buffer
[count
] = '\0';
987 /* Be careful in any case in this switch statement, never to call
988 * to more than a function that can generate text change notifications.
989 * Only one text change notification should be sent in any case.
990 * Else hazardous things can happen.
991 * Maybe we need a better solution than the function wrapper to inform
992 * functions that change text in text fields, if they need to send a
993 * change notification or not. -Dan
997 #ifdef XK_ISO_Left_Tab
998 case XK_ISO_Left_Tab
:
1000 if (event
->xkey
.state
& ShiftMask
) {
1001 if (tPtr
->view
->prevFocusChain
) {
1002 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1003 tPtr
->view
->prevFocusChain
);
1004 tPtr
->flags
.notIllegalMovement
= 1;
1006 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
1007 (void*)WMBacktabTextMovement
);
1009 if (tPtr
->view
->nextFocusChain
) {
1010 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
1011 tPtr
->view
->nextFocusChain
);
1012 tPtr
->flags
.notIllegalMovement
= 1;
1014 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
1015 (void*)WMTabTextMovement
);
1020 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
1021 (void*)WMReturnTextMovement
);
1024 case WM_EMACSKEY_LEFT
:
1025 if (!control_pressed
) {
1032 if (tPtr
->cursorPosition
> 0) {
1034 if (event
->xkey
.state
& ControlMask
) {
1035 int i
= tPtr
->cursorPosition
- 1;
1037 while (i
> 0 && tPtr
->text
[i
] != ' ') i
--;
1038 while (i
> 0 && tPtr
->text
[i
] == ' ') i
--;
1040 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1042 tPtr
->cursorPosition
--;
1044 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1045 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1050 if (event
->xkey
.state
& ShiftMask
)
1051 cancelSelection
= 0;
1054 case WM_EMACSKEY_RIGHT
:
1055 if (!control_pressed
) {
1062 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1064 if (event
->xkey
.state
& ControlMask
) {
1065 int i
= tPtr
->cursorPosition
;
1067 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ') i
++;
1068 while (tPtr
->text
[i
] == ' ') i
++;
1070 tPtr
->cursorPosition
= i
;
1072 tPtr
->cursorPosition
++;
1074 while (WMWidthOfString(tPtr
->font
,
1075 &(tPtr
->text
[tPtr
->viewPosition
]),
1076 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1077 > tPtr
->usableWidth
) {
1078 tPtr
->viewPosition
++;
1084 if (event
->xkey
.state
& ShiftMask
)
1085 cancelSelection
= 0;
1088 case WM_EMACSKEY_HOME
:
1089 if (!control_pressed
) {
1096 if (tPtr
->cursorPosition
> 0) {
1098 tPtr
->cursorPosition
= 0;
1099 if (tPtr
->viewPosition
> 0) {
1100 tPtr
->viewPosition
= 0;
1105 if (event
->xkey
.state
& ShiftMask
)
1106 cancelSelection
= 0;
1109 case WM_EMACSKEY_END
:
1110 if (!control_pressed
) {
1117 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1119 tPtr
->cursorPosition
= tPtr
->textLen
;
1120 tPtr
->viewPosition
= 0;
1121 while (WMWidthOfString(tPtr
->font
,
1122 &(tPtr
->text
[tPtr
->viewPosition
]),
1123 tPtr
->textLen
-tPtr
->viewPosition
)
1124 > tPtr
->usableWidth
) {
1125 tPtr
->viewPosition
++;
1131 if (event
->xkey
.state
& ShiftMask
)
1132 cancelSelection
= 0;
1135 case WM_EMACSKEY_BS
:
1136 if (!control_pressed
) {
1140 if (tPtr
->selection
.count
) {
1141 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1142 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1143 (void*)WMDeleteTextEvent
);
1144 } else if (tPtr
->cursorPosition
> 0) {
1146 range
.position
= tPtr
->cursorPosition
- 1;
1148 WMDeleteTextFieldRange(tPtr
, range
);
1149 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1150 (void*)WMDeleteTextEvent
);
1154 case WM_EMACSKEY_DEL
:
1155 if (!control_pressed
) {
1162 if (tPtr
->selection
.count
) {
1163 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1164 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1165 (void*)WMDeleteTextEvent
);
1166 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1168 range
.position
= tPtr
->cursorPosition
;
1170 WMDeleteTextFieldRange(tPtr
, range
);
1171 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1172 (void*)WMDeleteTextEvent
);
1178 if (count
> 0 && !iscntrl(buffer
[0])) {
1179 if (tPtr
->selection
.count
)
1180 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1181 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1182 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1183 (void*)WMInsertTextEvent
);
1189 if (!cancelSelection
) {
1190 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1191 WMNotification
*notif
;
1193 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1195 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1196 XA_PRIMARY
, tPtr
->view
->window
,
1197 event
->xbutton
.time
);
1198 notif
= WMCreateNotification("_lostOwnership", NULL
,tPtr
);
1199 WMPostNotification(notif
);
1200 WMReleaseNotification(notif
);
1205 if (tPtr
->selection
.count
) {
1206 tPtr
->selection
.count
= 0;
1209 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1213 paintTextField(tPtr
);
1215 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1220 pointToCursorPosition(TextField
*tPtr
, int x
)
1225 if (tPtr
->flags
.bordered
)
1228 a
= tPtr
->viewPosition
;
1229 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1230 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1231 tPtr
->textLen
- tPtr
->viewPosition
) < x
)
1232 return tPtr
->textLen
;
1234 while (a
< b
&& b
-a
>1) {
1236 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1237 mid
- tPtr
->viewPosition
);
1250 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1252 TextField
*tPtr
= (TextField
*)data
;
1253 static int move
= 0;
1254 static Time lastButtonReleasedEvent
= 0;
1256 CHECK_CLASS(data
, WC_TextField
);
1258 switch (event
->type
) {
1260 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1261 handleTextFieldKeyPress(tPtr
, event
);
1262 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1263 W_VIEW(tPtr
)->window
, False
,
1264 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1265 GrabModeAsync
, GrabModeAsync
, None
,
1266 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1268 tPtr
->flags
.pointerGrabbed
= 1;
1274 if (tPtr
->flags
.pointerGrabbed
) {
1275 tPtr
->flags
.pointerGrabbed
= 0;
1276 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1279 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1281 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1282 tPtr
->usableWidth
) {
1283 if (WMWidthOfString(tPtr
->font
,
1284 &(tPtr
->text
[tPtr
->viewPosition
]),
1285 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1286 > tPtr
->usableWidth
) {
1287 tPtr
->viewPosition
++;
1289 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1291 tPtr
->viewPosition
--;
1294 /*if (!tPtr->selection.count) {
1295 tPtr->selection.position = tPtr->cursorPosition;
1298 tPtr
->cursorPosition
=
1299 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1301 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1302 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1305 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1309 paintTextField(tPtr
);
1313 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1314 XA_PRIMARY
, tPtr
->view
->window
, event
->xmotion
.time
);
1316 WMNotification
*notif
= WMCreateNotification("_lostOwnership",
1318 WMPostNotification(notif
);
1319 WMReleaseNotification(notif
);
1325 if (tPtr
->flags
.pointerGrabbed
) {
1326 tPtr
->flags
.pointerGrabbed
= 0;
1327 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1332 switch (tPtr
->flags
.alignment
) {
1335 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1336 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1337 WMSetFocusToWidget(tPtr
);
1338 } else if (tPtr
->flags
.focused
) {
1339 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1340 tPtr
->selection
.count
= 0;
1342 if(textWidth
< tPtr
->usableWidth
){
1343 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1344 event
->xbutton
.x
- tPtr
->usableWidth
1347 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1350 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1352 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1355 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1358 paintTextField(tPtr
);
1362 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1363 WMSetFocusToWidget(tPtr
);
1364 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1366 paintTextField(tPtr
);
1367 } else if (tPtr
->flags
.focused
) {
1368 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1370 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1371 tPtr
->selection
.count
= 0;
1372 paintTextField(tPtr
);
1374 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1377 text
= W_GetTextSelection(tPtr
->view
->screen
, XA_PRIMARY
);
1380 text
= W_GetTextSelection(tPtr
->view
->screen
,
1381 tPtr
->view
->screen
->clipboardAtom
);
1384 text
= W_GetTextSelection(tPtr
->view
->screen
,
1388 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1390 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1391 (void*)WMInsertTextEvent
);
1399 if (tPtr
->flags
.pointerGrabbed
) {
1400 tPtr
->flags
.pointerGrabbed
= 0;
1401 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1406 if (event
->xbutton
.time
- lastButtonReleasedEvent
1407 <= WINGsConfiguration
.doubleClickDelay
) {
1408 tPtr
->selection
.position
= 0;
1409 tPtr
->selection
.count
= tPtr
->textLen
;
1410 paintTextField(tPtr
);
1411 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1412 XA_PRIMARY
, tPtr
->view
->window
, event
->xbutton
.time
);
1414 WMNotification
*notif
= WMCreateNotification("_lostOwnership",
1416 WMPostNotification(notif
);
1417 WMReleaseNotification(notif
);
1420 lastButtonReleasedEvent
= event
->xbutton
.time
;
1428 destroyTextField(TextField
*tPtr
)
1432 WMDeleteTimerHandler(tPtr
->timerID
);
1435 WMReleaseFont(tPtr
->font
);
1436 WMDeleteSelectionHandler(tPtr
, XA_PRIMARY
);
1437 WMRemoveNotificationObserver(tPtr
);