7 #include <X11/keysym.h>
12 #define CURSOR_BLINK_ON_DELAY 600
13 #define CURSOR_BLINK_OFF_DELAY 300
16 typedef struct WMTextFieldDelegate
{
19 void (*didBeginEditing
)(struct WMTextFieldDelegate
*self
,
20 WMNotification
*notif
);
22 void (*didChange
)(struct WMTextFieldDelegate
*self
,
23 WMNotification
*notif
);
25 void (*didEndEditing
)(struct WMTextFieldDelegate
*self
,
26 WMNotification
*notif
);
28 Bool (*shouldBeginEditing
)(struct WMTextFieldDelegate
*self
,
31 Bool (*shouldEndEditing
)(struct WMTextFieldDelegate
*self
,
33 } WMTextFieldDelegate
;
37 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
38 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
39 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
42 typedef struct W_TextField
{
47 struct W_TextField
*nextField
; /* next textfield in the chain */
48 struct W_TextField
*prevField
;
52 int textLen
; /* size of text */
53 int bufferSize
; /* memory allocated for text */
55 int viewPosition
; /* position of text being shown */
57 int cursorPosition
; /* position of the insertion cursor */
60 short offsetWidth
; /* offset of text from border */
63 WMRange prevselection
;
67 WMTextFieldDelegate
*delegate
;
70 WMHandlerID timerID
; /* for cursor blinking */
73 WMAlignment alignment
:2;
75 unsigned int bordered
:1;
77 unsigned int beveled
:1;
79 unsigned int enabled
:1;
81 unsigned int focused
:1;
83 unsigned int cursorOn
:1;
85 unsigned int secure
:1; /* password entry style */
87 unsigned int pointerGrabbed
:1;
90 unsigned int notIllegalMovement
:1;
95 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
96 if ((T)->delegate && (T)->delegate->C)\
97 (*(T)->delegate->C)((T)->delegate,notif);\
98 WMPostNotification(notif);\
99 WMReleaseNotification(notif);}
102 #define MIN_TEXT_BUFFER 2
103 #define TEXT_BUFFER_INCR 8
106 #define WM_EMACSKEYMASK ControlMask
108 #define WM_EMACSKEY_LEFT XK_b
109 #define WM_EMACSKEY_RIGHT XK_f
110 #define WM_EMACSKEY_HOME XK_a
111 #define WM_EMACSKEY_END XK_e
112 #define WM_EMACSKEY_BS XK_h
113 #define WM_EMACSKEY_DEL XK_d
117 #define DEFAULT_WIDTH 60
118 #define DEFAULT_HEIGHT 20
119 #define DEFAULT_BORDERED True
120 #define DEFAULT_ALIGNMENT WALeft
124 static void destroyTextField(TextField
*tPtr
);
125 static void paintTextField(TextField
*tPtr
);
127 static void handleEvents(XEvent
*event
, void *data
);
128 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
129 static void resizeTextField();
131 struct W_ViewProcedureTable _TextFieldViewProcedures
= {
138 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
139 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
141 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
142 &((tPtr)->text[(start)]), (end) - (start) + 1))
146 memmv(char *dest
, char *src
, int size
)
151 for (i
=size
-1; i
>=0; i
--) {
154 } else if (dest
< src
) {
155 for (i
=0; i
<size
; i
++) {
163 incrToFit(TextField
*tPtr
)
165 int vp
= tPtr
->viewPosition
;
167 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
168 tPtr
->viewPosition
++;
170 return vp
!=tPtr
->viewPosition
;
175 incrToFit2(TextField
*tPtr
)
177 int vp
= tPtr
->viewPosition
;
178 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
179 >= tPtr
->usableWidth
)
180 tPtr
->viewPosition
++;
183 return vp
!=tPtr
->viewPosition
;
188 decrToFit(TextField
*tPtr
)
190 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
191 && tPtr
->viewPosition
>0)
192 tPtr
->viewPosition
--;
200 WMCreateTextField(WMWidget
*parent
)
204 tPtr
= wmalloc(sizeof(TextField
));
205 memset(tPtr
, 0, sizeof(TextField
));
207 tPtr
->widgetClass
= WC_TextField
;
209 tPtr
->view
= W_CreateView(W_VIEW(parent
));
214 tPtr
->view
->self
= tPtr
;
216 tPtr
->view
->attribFlags
|= CWCursor
;
217 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
219 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
221 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
224 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
226 tPtr
->flags
.enabled
= 1;
228 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
229 |FocusChangeMask
, handleEvents
, tPtr
);
231 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
233 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
235 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
236 tPtr
->flags
.beveled
= True
;
237 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
239 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
241 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
242 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
243 handleTextFieldActionEvents
, tPtr
);
245 tPtr
->flags
.cursorOn
= 1;
252 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
254 tPtr
->delegate
= delegate
;
259 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
263 CHECK_CLASS(tPtr
, WC_TextField
);
270 /* check if buffer will hold the text */
271 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
272 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
273 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
276 if (position
< 0 || position
>= tPtr
->textLen
) {
277 /* append the text at the end */
278 strcat(tPtr
->text
, text
);
282 tPtr
->textLen
+= len
;
283 tPtr
->cursorPosition
+= len
;
285 /* insert text at position */
286 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
287 tPtr
->textLen
-position
+1);
289 memcpy(&(tPtr
->text
[position
]), text
, len
);
291 tPtr
->textLen
+= len
;
292 if (position
>= tPtr
->cursorPosition
) {
293 tPtr
->cursorPosition
+= len
;
300 paintTextField(tPtr
);
302 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
303 (void*)WMInsertTextEvent
);
308 deleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
310 CHECK_CLASS(tPtr
, WC_TextField
);
312 if (range
.position
>= tPtr
->textLen
)
315 if (range
.count
< 1) {
316 if (range
.position
< 0)
318 tPtr
->text
[range
.position
] = 0;
319 tPtr
->textLen
= range
.position
;
321 tPtr
->cursorPosition
= 0;
322 tPtr
->viewPosition
= 0;
324 if (range
.position
+ range
.count
> tPtr
->textLen
)
325 range
.count
= tPtr
->textLen
- range
.position
;
326 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
327 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
328 tPtr
->textLen
-= range
.count
;
330 if (tPtr
->cursorPosition
> range
.position
)
331 tPtr
->cursorPosition
-= range
.count
;
336 paintTextField(tPtr
);
341 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
343 deleteTextFieldRange(tPtr
, range
);
344 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
345 (void*)WMDeleteTextEvent
);
351 WMGetTextFieldText(WMTextField
*tPtr
)
353 CHECK_CLASS(tPtr
, WC_TextField
);
355 return wstrdup(tPtr
->text
);
360 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
362 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
363 (!text
&& tPtr
->textLen
== 0))
370 tPtr
->textLen
= strlen(text
);
372 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
373 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
374 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
376 strcpy(tPtr
->text
, text
);
379 if (tPtr->textLen < tPtr->cursorPosition)
380 tPtr->cursorPosition = tPtr->textLen;
382 tPtr
->cursorPosition
= tPtr
->textLen
;
383 tPtr
->viewPosition
= 0;
384 tPtr
->selection
.count
= 0;
386 if (tPtr
->view
->flags
.realized
)
387 paintTextField(tPtr
);
389 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
390 (void*)WMSetTextEvent
);
395 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
397 /* TODO: update font change after field is mapped */
398 WMReleaseFont(tPtr
->font
);
399 tPtr
->font
= WMRetainFont(font
);
404 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
406 tPtr
->flags
.alignment
= alignment
;
407 if (alignment
!=WALeft
) {
408 wwarning("only left alignment is supported in textfields");
412 if (tPtr
->view
->flags
.realized
) {
413 paintTextField(tPtr
);
419 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
421 tPtr
->flags
.bordered
= bordered
;
423 if (tPtr
->view
->flags
.realized
) {
424 paintTextField(tPtr
);
430 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
432 tPtr
->flags
.beveled
= flag
;
434 if (tPtr
->view
->flags
.realized
) {
435 paintTextField(tPtr
);
442 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
444 tPtr
->flags
.secure
= flag
;
446 if (tPtr
->view
->flags
.realized
) {
447 paintTextField(tPtr
);
453 WMGetTextFieldEditable(WMTextField
*tPtr
)
455 return tPtr
->flags
.enabled
;
460 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
462 tPtr
->flags
.enabled
= flag
;
464 if (tPtr
->view
->flags
.realized
) {
465 paintTextField(tPtr
);
471 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
473 if (tPtr
->flags
.enabled
) {
474 if (range
.position
< 0) {
475 range
.count
+= range
.position
;
476 range
.count
= (range
.count
< 0) ? 0 : range
.count
;
478 } else if (range
.position
> tPtr
->textLen
) {
479 range
.position
= tPtr
->textLen
;
483 if (range
.position
+ range
.count
> tPtr
->textLen
)
484 range
.count
= tPtr
->textLen
- range
.position
;
486 tPtr
->prevselection
= tPtr
->selection
; /* check if this is needed */
488 tPtr
->selection
= range
;
490 if (tPtr
->view
->flags
.realized
) {
491 paintTextField(tPtr
);
498 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
500 if (tPtr
->flags
.enabled
) {
501 if (position
> tPtr
->textLen
)
502 position
= tPtr
->textLen
;
504 tPtr
->cursorPosition
= position
;
505 if (tPtr
->view
->flags
.realized
) {
506 paintTextField(tPtr
);
513 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
515 CHECK_CLASS(tPtr
, WC_TextField
);
517 if (tPtr
->view
->nextFocusChain
)
518 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
519 tPtr
->view
->nextFocusChain
= NULL
;
523 CHECK_CLASS(next
, WC_TextField
);
525 if (tPtr
->view
->nextFocusChain
)
526 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
527 if (next
->view
->prevFocusChain
)
528 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
530 tPtr
->view
->nextFocusChain
= next
->view
;
531 next
->view
->prevFocusChain
= tPtr
->view
;
536 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
538 CHECK_CLASS(tPtr
, WC_TextField
);
540 if (tPtr
->view
->prevFocusChain
)
541 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
542 tPtr
->view
->prevFocusChain
= NULL
;
546 CHECK_CLASS(prev
, WC_TextField
);
548 if (tPtr
->view
->prevFocusChain
)
549 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
550 if (prev
->view
->nextFocusChain
)
551 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
553 tPtr
->view
->prevFocusChain
= prev
->view
;
554 prev
->view
->nextFocusChain
= tPtr
->view
;
559 resizeTextField(WMTextField
*tPtr
, unsigned int width
, unsigned int height
)
561 W_ResizeView(tPtr
->view
, width
, height
);
564 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
566 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
571 makeHiddenString(int length
)
573 char *data
= wmalloc(length
+1);
575 memset(data
, '*', length
);
582 paintCursor(TextField
*tPtr
)
585 WMScreen
*screen
= tPtr
->view
->screen
;
589 if (tPtr
->flags
.secure
)
590 text
= makeHiddenString(strlen(tPtr
->text
));
594 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
595 tPtr
->cursorPosition
-tPtr
->viewPosition
);
597 switch (tPtr
->flags
.alignment
) {
599 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
600 if (textWidth
< tPtr
->usableWidth
)
601 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
603 cx
+= tPtr
->offsetWidth
+ 1;
606 cx
+= tPtr
->offsetWidth
+ 1;
611 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
612 if (textWidth
< tPtr
->usableWidth
)
613 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
615 cx
+= tPtr
->offsetWidth
;
619 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
620 cx, tPtr->offsetWidth, 1,
621 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
622 printf("%d %d\n",cx,tPtr->cursorPosition);
624 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
625 cx
, tPtr
->offsetWidth
, cx
,
626 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
628 if (tPtr
->flags
.secure
)
635 drawRelief(WMView
*view
, Bool beveled
)
637 WMScreen
*scr
= view
->screen
;
638 Display
*dpy
= scr
->display
;
642 int width
= view
->size
.width
;
643 int height
= view
->size
.height
;
645 dgc
= WMColorGC(scr
->darkGray
);
648 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
652 wgc
= WMColorGC(scr
->white
);
653 lgc
= WMColorGC(scr
->gray
);
656 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
657 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
659 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
660 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
663 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
664 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
666 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
667 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
672 paintTextField(TextField
*tPtr
)
674 W_Screen
*screen
= tPtr
->view
->screen
;
675 W_View
*view
= tPtr
->view
;
685 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
688 if (!tPtr
->flags
.bordered
) {
694 if (tPtr
->flags
.secure
) {
695 text
= makeHiddenString(strlen(tPtr
->text
));
700 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
702 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
703 view
->size
.width
, view
->size
.height
, screen
->depth
);
704 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
705 0,0, view
->size
.width
,view
->size
.height
);
706 /* this is quite dirty */
707 viewbuffer
.screen
= view
->screen
;
708 viewbuffer
.size
= view
->size
;
709 viewbuffer
.window
= drawbuffer
;
712 if (tPtr
->textLen
> 0) {
713 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
714 tPtr
->textLen
- tPtr
->viewPosition
);
716 th
= WMFontHeight(tPtr
->font
);
718 ty
= tPtr
->offsetWidth
;
719 switch (tPtr
->flags
.alignment
) {
721 tx
= tPtr
->offsetWidth
+ 1;
722 if (tw
< tPtr
->usableWidth
)
723 XFillRectangle(screen
->display
, drawbuffer
,
724 WMColorGC(screen
->white
),
725 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
729 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
730 if (tw
< tPtr
->usableWidth
)
731 XClearArea(screen
->display
, view
->window
, bd
, bd
,
732 totalWidth
, view
->size
.height
-2*bd
, False
);
737 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
738 if (tw
< tPtr
->usableWidth
)
739 XClearArea(screen
->display
, view
->window
, bd
, bd
,
740 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
744 if (!tPtr
->flags
.enabled
)
745 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
747 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
749 &(text
[tPtr
->viewPosition
]),
750 tPtr
->textLen
- tPtr
->viewPosition
);
752 if (tPtr
->selection
.count
) {
755 count
= tPtr
->selection
.count
< 0
756 ? tPtr
->selection
.position
+ tPtr
->selection
.count
757 : tPtr
->selection
.position
;
758 count2
= abs(tPtr
->selection
.count
);
759 if (count
< tPtr
->viewPosition
) {
760 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
761 count
= tPtr
->viewPosition
;
765 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
766 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
768 XSetBackground(screen
->display
, screen
->textFieldGC
,
769 screen
->gray
->color
.pixel
);
771 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
772 tPtr
->font
, rx
, ty
, &(text
[count
]),
775 XSetBackground(screen
->display
, screen
->textFieldGC
,
776 screen
->white
->color
.pixel
);
779 if (!tPtr
->flags
.enabled
)
780 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
782 XFillRectangle(screen
->display
, drawbuffer
,
783 WMColorGC(screen
->white
),
784 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
788 if (tPtr
->flags
.bordered
) {
789 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
792 if (tPtr
->flags
.secure
)
794 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
795 screen
->copyGC
, 0,0, view
->size
.width
,
796 view
->size
.height
,0,0);
797 XFreePixmap(screen
->display
, drawbuffer
);
800 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
808 blinkCursor(void *data
)
810 TextField
*tPtr
= (TextField
*)data
;
812 if (tPtr
->flags
.cursorOn
) {
813 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
816 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
820 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
826 handleEvents(XEvent
*event
, void *data
)
828 TextField
*tPtr
= (TextField
*)data
;
830 CHECK_CLASS(data
, WC_TextField
);
833 switch (event
->type
) {
835 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
837 tPtr
->flags
.focused
= 1;
839 if (!tPtr
->timerID
) {
840 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
844 paintTextField(tPtr
);
846 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
848 tPtr
->flags
.notIllegalMovement
= 0;
852 tPtr
->flags
.focused
= 0;
855 WMDeleteTimerHandler(tPtr
->timerID
);
856 tPtr
->timerID
= NULL
;
859 paintTextField(tPtr
);
860 if (!tPtr
->flags
.notIllegalMovement
) {
861 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
862 (void*)WMIllegalTextMovement
);
867 if (event
->xexpose
.count
!=0)
869 paintTextField(tPtr
);
873 destroyTextField(tPtr
);
880 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
884 int count
, refresh
= 0;
885 int control_pressed
= 0;
887 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
) {
891 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
892 buffer
[count
] = '\0';
894 if (!(event
->xkey
.state
& ShiftMask
)) {
895 if (tPtr
->selection
.count
)
897 tPtr
->prevselection
= tPtr
->selection
;
898 tPtr
->selection
.position
= tPtr
->cursorPosition
;
899 tPtr
->selection
.count
= 0;
902 /* Be careful in any case in this switch statement, never to call
903 * to more than a function that can generate text change notifications.
904 * Only one text change notification should be sent in any case.
905 * Else hazardous things can happen.
906 * Maybe we need a better solution than the function wrapper to inform
907 * functions that change text in text fields, if they need to send a
908 * change notification or not. -Dan
912 case XK_ISO_Left_Tab
:
913 if (event
->xkey
.state
& ShiftMask
) {
914 if (tPtr
->view
->prevFocusChain
) {
915 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
916 tPtr
->view
->prevFocusChain
);
917 tPtr
->flags
.notIllegalMovement
= 1;
919 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
920 (void*)WMBacktabTextMovement
);
922 if (tPtr
->view
->nextFocusChain
) {
923 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
924 tPtr
->view
->nextFocusChain
);
925 tPtr
->flags
.notIllegalMovement
= 1;
927 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
928 (void*)WMTabTextMovement
);
933 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
934 (void*)WMReturnTextMovement
);
937 case WM_EMACSKEY_LEFT
:
938 if (!control_pressed
) {
943 if (tPtr
->cursorPosition
> 0) {
945 if (event
->xkey
.state
& ControlMask
) {
947 for (i
= tPtr
->cursorPosition
- 1; i
>= 0; i
--)
948 if (tPtr
->text
[i
] == ' ' || i
== 0) {
949 tPtr
->cursorPosition
= i
;
953 tPtr
->cursorPosition
--;
955 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
956 tPtr
->viewPosition
= tPtr
->cursorPosition
;
964 case WM_EMACSKEY_RIGHT
:
965 if (!control_pressed
) {
970 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
972 if (event
->xkey
.state
& ControlMask
) {
974 for (i
= tPtr
->cursorPosition
+ 1; i
<= tPtr
->textLen
; i
++)
975 if (tPtr
->text
[i
] == ' ' || i
== tPtr
->textLen
) {
976 tPtr
->cursorPosition
= i
;
980 tPtr
->cursorPosition
++;
982 while (WMWidthOfString(tPtr
->font
,
983 &(tPtr
->text
[tPtr
->viewPosition
]),
984 tPtr
->cursorPosition
-tPtr
->viewPosition
)
985 > tPtr
->usableWidth
) {
986 tPtr
->viewPosition
++;
994 case WM_EMACSKEY_HOME
:
995 if (!control_pressed
) {
1000 if (tPtr
->cursorPosition
> 0) {
1002 tPtr
->cursorPosition
= 0;
1003 if (tPtr
->viewPosition
> 0) {
1004 tPtr
->viewPosition
= 0;
1012 case WM_EMACSKEY_END
:
1013 if (!control_pressed
) {
1018 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1020 tPtr
->cursorPosition
= tPtr
->textLen
;
1021 tPtr
->viewPosition
= 0;
1022 while (WMWidthOfString(tPtr
->font
,
1023 &(tPtr
->text
[tPtr
->viewPosition
]),
1024 tPtr
->textLen
-tPtr
->viewPosition
)
1025 > tPtr
->usableWidth
) {
1026 tPtr
->viewPosition
++;
1034 case WM_EMACSKEY_BS
:
1035 if (!control_pressed
) {
1039 if (tPtr
->cursorPosition
> 0) {
1042 if (tPtr
->prevselection
.count
) {
1043 range
.position
= tPtr
->prevselection
.count
< 0
1044 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1045 : tPtr
->prevselection
.position
;
1047 range
.count
= abs(tPtr
->prevselection
.count
);
1049 range
.position
= tPtr
->cursorPosition
- 1;
1052 WMDeleteTextFieldRange(tPtr
, range
);
1056 case WM_EMACSKEY_DEL
:
1057 if (!control_pressed
) {
1062 if (tPtr
->cursorPosition
< tPtr
->textLen
|| tPtr
->prevselection
.count
) {
1065 if (tPtr
->prevselection
.count
) {
1066 range
.position
= tPtr
->prevselection
.count
< 0
1067 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1068 : tPtr
->prevselection
.position
;
1070 range
.count
= abs(tPtr
->prevselection
.count
);
1072 range
.position
= tPtr
->cursorPosition
;
1075 WMDeleteTextFieldRange(tPtr
, range
);
1081 if (count
> 0 && !iscntrl(buffer
[0])) {
1084 if (tPtr
->prevselection
.count
) {
1085 range
.position
= tPtr
->prevselection
.count
< 0
1086 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1087 : tPtr
->prevselection
.position
;
1089 range
.count
= abs(tPtr
->prevselection
.count
);
1091 range
.position
= tPtr
->cursorPosition
;
1094 if (tPtr
->prevselection
.count
)
1095 deleteTextFieldRange(tPtr
, range
);
1096 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1102 if (event
->xkey
.state
& ShiftMask
) {
1103 if (tPtr
->selection
.count
== 0)
1104 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1105 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1108 tPtr
->prevselection
.count
= 0;
1110 paintTextField(tPtr
);
1116 pointToCursorPosition(TextField
*tPtr
, int x
)
1121 if (tPtr
->flags
.bordered
)
1124 a
= tPtr
->viewPosition
;
1125 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1126 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1127 tPtr
->textLen
-tPtr
->viewPosition
) < x
)
1128 return tPtr
->textLen
;
1130 while (a
< b
&& b
-a
>1) {
1132 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1133 mid
- tPtr
->viewPosition
);
1146 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1148 TextField
*tPtr
= (TextField
*)data
;
1151 CHECK_CLASS(data
, WC_TextField
);
1153 switch (event
->type
) {
1155 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1156 handleTextFieldKeyPress(tPtr
, event
);
1157 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1158 W_VIEW(tPtr
)->window
, False
,
1159 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1160 GrabModeAsync
, GrabModeAsync
, None
,
1161 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1163 tPtr
->flags
.pointerGrabbed
= 1;
1169 if (tPtr
->flags
.pointerGrabbed
) {
1170 tPtr
->flags
.pointerGrabbed
= 0;
1171 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1174 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1176 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1177 tPtr
->usableWidth
) {
1178 if (WMWidthOfString(tPtr
->font
,
1179 &(tPtr
->text
[tPtr
->viewPosition
]),
1180 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1181 > tPtr
->usableWidth
) {
1182 tPtr
->viewPosition
++;
1184 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1186 tPtr
->viewPosition
--;
1189 if (!tPtr
->selection
.count
) {
1190 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1193 tPtr
->cursorPosition
=
1194 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1196 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1199 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1203 paintTextField(tPtr
);
1208 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1209 XA_PRIMARY
, None
, CurrentTime
);
1210 count
= tPtr
->selection
.count
< 0
1211 ? tPtr
->selection
.position
+ tPtr
->selection
.count
1212 : tPtr
->selection
.position
;
1213 XStoreBuffer(tPtr
->view
->screen
->display
,
1214 &tPtr
->text
[count
] , abs(tPtr
->selection
.count
), 0);
1219 if (tPtr
->flags
.pointerGrabbed
) {
1220 tPtr
->flags
.pointerGrabbed
= 0;
1221 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1226 switch (tPtr
->flags
.alignment
) {
1229 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1230 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1231 WMSetFocusToWidget(tPtr
);
1232 } else if (tPtr
->flags
.focused
) {
1233 tPtr
->selection
.count
= 0;
1235 if(textWidth
< tPtr
->usableWidth
){
1236 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1237 event
->xbutton
.x
- tPtr
->usableWidth
1240 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1243 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1245 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1248 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1251 paintTextField(tPtr
);
1255 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1256 WMSetFocusToWidget(tPtr
);
1257 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1259 paintTextField(tPtr
);
1260 } else if (tPtr
->flags
.focused
) {
1261 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1263 tPtr
->selection
.count
= 0;
1264 paintTextField(tPtr
);
1266 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1269 text
= W_GetTextSelection(tPtr
->view
->screen
,
1270 tPtr
->view
->screen
->clipboardAtom
);
1272 text
= W_GetTextSelection(tPtr
->view
->screen
,
1276 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1278 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, NULL
);
1286 if (tPtr
->flags
.pointerGrabbed
) {
1287 tPtr
->flags
.pointerGrabbed
= 0;
1288 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1298 destroyTextField(TextField
*tPtr
)
1302 WMDeleteTimerHandler(tPtr
->timerID
);
1305 WMReleaseFont(tPtr
->font
);