5 #include <X11/keysym.h>
10 #define CURSOR_BLINK_ON_DELAY 600
11 #define CURSOR_BLINK_OFF_DELAY 300
13 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
14 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
15 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
17 typedef struct W_TextField
{
22 struct W_TextField
*nextField
; /* next textfield in the chain */
23 struct W_TextField
*prevField
;
27 int textLen
; /* size of text */
28 int bufferSize
; /* memory allocated for text */
30 int viewPosition
; /* position of text being shown */
32 int cursorPosition
; /* position of the insertion cursor */
35 short offsetWidth
; /* offset of text from border */
41 WMTextFieldDelegate
*delegate
;
44 WMHandlerID timerID
; /* for cursor blinking */
47 WMAlignment alignment
:2;
49 unsigned int bordered
:1;
51 unsigned int beveled
:1;
53 unsigned int enabled
:1;
55 unsigned int focused
:1;
57 unsigned int cursorOn
:1;
59 unsigned int secure
:1; /* password entry style */
61 unsigned int pointerGrabbed
:1;
63 unsigned int ownsSelection
:1;
65 unsigned int waitingSelection
:1; /* requested selection, but
68 unsigned int notIllegalMovement
:1;
72 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
73 if ((T)->delegate && (T)->delegate->C)\
74 (*(T)->delegate->C)((T)->delegate,notif);\
75 WMPostNotification(notif);\
76 WMReleaseNotification(notif);}
78 #define MIN_TEXT_BUFFER 2
79 #define TEXT_BUFFER_INCR 8
81 #define WM_EMACSKEYMASK ControlMask
83 #define WM_EMACSKEY_LEFT XK_b
84 #define WM_EMACSKEY_RIGHT XK_f
85 #define WM_EMACSKEY_HOME XK_a
86 #define WM_EMACSKEY_END XK_e
87 #define WM_EMACSKEY_BS XK_h
88 #define WM_EMACSKEY_DEL XK_d
90 #define DEFAULT_WIDTH 60
91 #define DEFAULT_HEIGHT 20
92 #define DEFAULT_BORDERED True
93 #define DEFAULT_ALIGNMENT WALeft
95 static void destroyTextField(TextField
* tPtr
);
96 static void paintTextField(TextField
* tPtr
);
98 static void handleEvents(XEvent
* event
, void *data
);
99 static void handleTextFieldActionEvents(XEvent
* event
, void *data
);
100 static void didResizeTextField();
102 struct W_ViewDelegate _TextFieldViewDelegate
= {
110 static void lostSelection(WMView
* view
, Atom selection
, void *cdata
);
112 static WMData
*requestHandler(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
);
114 static WMSelectionProcs selectionHandler
= {
120 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
121 &((tPtr)->text[(start)]), (tPtr)->textLen - (start)))
123 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
124 &((tPtr)->text[(start)]), (end) - (start)))
126 static INLINE
int oneUTF8CharBackward(char *str
, int len
)
128 unsigned char *ustr
= (unsigned char *)str
;
131 while (len
-- > 0 && ustr
[--pos
] >= 0x80 && ustr
[pos
] <= 0xbf) ;
135 static INLINE
int oneUTF8CharForward(char *str
, int len
)
137 unsigned char *ustr
= (unsigned char *)str
;
140 while (len
-- > 0 && ustr
[++pos
] >= 0x80 && ustr
[pos
] <= 0xbf) ;
144 // find the beginning of the UTF8 char pointed by str
145 static INLINE
int seekUTF8CharStart(char *str
, int len
)
147 unsigned char *ustr
= (unsigned char *)str
;
150 while (len
-- > 0 && ustr
[pos
] >= 0x80 && ustr
[pos
] <= 0xbf)
155 static void normalizeRange(TextField
* tPtr
, WMRange
* range
)
157 if (range
->position
< 0 && range
->count
< 0)
160 if (range
->count
== 0) {
161 /*range->position = 0; why is this? */
165 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
166 if (range
->count
< 0) { /* && range->position >= 0 */
167 if (range
->position
+ range
->count
< 0) {
168 range
->count
= range
->position
;
171 range
->count
= -range
->count
;
172 range
->position
-= range
->count
;
174 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
175 } else if (range
->position
< 0) { /* && range->count > 0 */
176 if (range
->position
+ range
->count
< 0) {
177 range
->position
= range
->count
= 0;
179 range
->count
+= range
->position
;
184 if (range
->position
+ range
->count
> tPtr
->textLen
)
185 range
->count
= tPtr
->textLen
- range
->position
;
188 static void memmv(char *dest
, char *src
, int size
)
193 for (i
= size
- 1; i
>= 0; i
--) {
196 } else if (dest
< src
) {
197 for (i
= 0; i
< size
; i
++) {
203 static int incrToFit(TextField
* tPtr
)
205 int vp
= tPtr
->viewPosition
;
207 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
208 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
209 tPtr
->textLen
- tPtr
->viewPosition
);
211 return vp
!= tPtr
->viewPosition
;
214 static int incrToFit2(TextField
* tPtr
)
216 int vp
= tPtr
->viewPosition
;
218 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
219 >= tPtr
->usableWidth
)
220 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
221 tPtr
->cursorPosition
- tPtr
->viewPosition
);
222 return vp
!= tPtr
->viewPosition
;
225 static void decrToFit(TextField
* tPtr
)
227 int vp
= tPtr
->viewPosition
;
229 while (vp
> 0 && (vp
+= oneUTF8CharBackward(&tPtr
->text
[vp
], vp
),
230 TEXT_WIDTH(tPtr
, vp
)) < tPtr
->usableWidth
) {
231 tPtr
->viewPosition
= vp
;
238 static WMData
*requestHandler(WMView
* view
, Atom selection
, Atom target
, void *cdata
, Atom
* type
)
240 TextField
*tPtr
= view
->self
;
242 Display
*dpy
= tPtr
->view
->screen
->display
;
244 Atom TEXT
= XInternAtom(dpy
, "TEXT", False
);
245 Atom COMPOUND_TEXT
= XInternAtom(dpy
, "COMPOUND_TEXT", False
);
248 count
= tPtr
->selection
.count
< 0
249 ? tPtr
->selection
.position
+ tPtr
->selection
.count
: tPtr
->selection
.position
;
251 if (target
== XA_STRING
|| target
== TEXT
|| target
== COMPOUND_TEXT
) {
253 data
= WMCreateDataWithBytes(&(tPtr
->text
[count
]), abs(tPtr
->selection
.count
));
254 WMSetDataFormat(data
, 8);
260 _TARGETS
= XInternAtom(dpy
, "TARGETS", False
);
261 if (target
== _TARGETS
) {
264 ptr
= wmalloc(4 * sizeof(Atom
));
268 ptr
[3] = COMPOUND_TEXT
;
270 data
= WMCreateDataWithBytes(ptr
, 4 * 4);
271 WMSetDataFormat(data
, 32);
281 static void lostSelection(WMView
* view
, Atom selection
, void *cdata
)
283 TextField
*tPtr
= (WMTextField
*) view
->self
;
285 if (tPtr
->flags
.ownsSelection
) {
286 WMDeleteSelectionHandler(view
, selection
, CurrentTime
);
287 tPtr
->flags
.ownsSelection
= 0;
289 if (tPtr
->selection
.count
!= 0) {
290 tPtr
->selection
.count
= 0;
291 paintTextField(tPtr
);
295 static void selectionNotification(void *observerData
, WMNotification
* notification
)
297 WMView
*observerView
= (WMView
*) observerData
;
298 WMView
*newOwnerView
= (WMView
*) WMGetNotificationClientData(notification
);
300 if (observerView
!= newOwnerView
) {
302 //if (tPtr->flags.ownsSelection)
303 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
305 lostSelection(observerView
, XA_PRIMARY
, NULL
);
309 static void realizeObserver(void *self
, WMNotification
* not)
311 W_CreateIC(((TextField
*) self
)->view
);
314 WMTextField
*WMCreateTextField(WMWidget
* parent
)
318 tPtr
= wmalloc(sizeof(TextField
));
319 memset(tPtr
, 0, sizeof(TextField
));
321 tPtr
->widgetClass
= WC_TextField
;
323 tPtr
->view
= W_CreateView(W_VIEW(parent
));
328 tPtr
->view
->self
= tPtr
;
330 tPtr
->view
->delegate
= &_TextFieldViewDelegate
;
332 tPtr
->view
->attribFlags
|= CWCursor
;
333 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
335 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
337 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
340 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
342 tPtr
->flags
.enabled
= 1;
344 WMCreateEventHandler(tPtr
->view
, ExposureMask
| StructureNotifyMask
| FocusChangeMask
, handleEvents
, tPtr
);
346 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
348 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
349 tPtr
->flags
.beveled
= True
;
350 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
351 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
353 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
355 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
| LeaveWindowMask
356 | ButtonReleaseMask
| ButtonPressMask
| KeyPressMask
| Button1MotionMask
,
357 handleTextFieldActionEvents
, tPtr
);
359 WMAddNotificationObserver(selectionNotification
, tPtr
->view
,
360 WMSelectionOwnerDidChangeNotification
, (void *)XA_PRIMARY
);
362 WMAddNotificationObserver(realizeObserver
, tPtr
, WMViewRealizedNotification
, tPtr
->view
);
364 tPtr
->flags
.cursorOn
= 1;
369 void WMSetTextFieldDelegate(WMTextField
* tPtr
, WMTextFieldDelegate
* delegate
)
371 CHECK_CLASS(tPtr
, WC_TextField
);
373 tPtr
->delegate
= delegate
;
376 WMTextFieldDelegate
*WMGetTextFieldDelegate(WMTextField
* tPtr
)
378 CHECK_CLASS(tPtr
, WC_TextField
);
380 return tPtr
->delegate
;
383 void WMInsertTextFieldText(WMTextField
* tPtr
, char *text
, int position
)
387 CHECK_CLASS(tPtr
, WC_TextField
);
394 /* check if buffer will hold the text */
395 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
396 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
397 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
400 if (position
< 0 || position
>= tPtr
->textLen
) {
401 /* append the text at the end */
402 strcat(tPtr
->text
, text
);
403 tPtr
->textLen
+= len
;
404 tPtr
->cursorPosition
+= len
;
407 /* insert text at position */
408 memmv(&(tPtr
->text
[position
+ len
]), &(tPtr
->text
[position
]), tPtr
->textLen
- position
+ 1);
410 memcpy(&(tPtr
->text
[position
]), text
, len
);
412 tPtr
->textLen
+= len
;
413 if (position
>= tPtr
->cursorPosition
) {
414 tPtr
->cursorPosition
+= len
;
421 paintTextField(tPtr
);
424 void WMDeleteTextFieldRange(WMTextField
* tPtr
, WMRange range
)
426 CHECK_CLASS(tPtr
, WC_TextField
);
428 normalizeRange(tPtr
, &range
);
433 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+ range
.count
]),
434 tPtr
->textLen
- (range
.position
+ range
.count
) + 1);
436 /* better than nothing ;) */
437 if (tPtr
->cursorPosition
> range
.position
)
438 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
], tPtr
->viewPosition
);
439 tPtr
->textLen
-= range
.count
;
440 tPtr
->cursorPosition
= range
.position
;
444 paintTextField(tPtr
);
447 char *WMGetTextFieldText(WMTextField
* tPtr
)
449 CHECK_CLASS(tPtr
, WC_TextField
);
451 return wstrdup(tPtr
->text
);
454 void WMSetTextFieldText(WMTextField
* tPtr
, char *text
)
456 CHECK_CLASS(tPtr
, WC_TextField
);
458 if ((text
&& strcmp(tPtr
->text
, text
) == 0) || (!text
&& tPtr
->textLen
== 0))
465 tPtr
->textLen
= strlen(text
);
467 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
468 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
469 tPtr
->text
= wrealloc(tPtr
->text
, tPtr
->bufferSize
);
471 strcpy(tPtr
->text
, text
);
474 tPtr
->cursorPosition
= tPtr
->selection
.position
= tPtr
->textLen
;
475 tPtr
->viewPosition
= 0;
476 tPtr
->selection
.count
= 0;
478 if (tPtr
->view
->flags
.realized
)
479 paintTextField(tPtr
);
482 void WMSetTextFieldAlignment(WMTextField
* tPtr
, WMAlignment alignment
)
484 CHECK_CLASS(tPtr
, WC_TextField
);
486 tPtr
->flags
.alignment
= alignment
;
488 if (alignment
!= WALeft
) {
489 wwarning("only left alignment is supported in textfields");
493 if (tPtr
->view
->flags
.realized
) {
494 paintTextField(tPtr
);
498 void WMSetTextFieldBordered(WMTextField
* tPtr
, Bool bordered
)
500 CHECK_CLASS(tPtr
, WC_TextField
);
502 tPtr
->flags
.bordered
= bordered
;
504 if (tPtr
->view
->flags
.realized
) {
505 paintTextField(tPtr
);
509 void WMSetTextFieldBeveled(WMTextField
* tPtr
, Bool flag
)
511 CHECK_CLASS(tPtr
, WC_TextField
);
513 tPtr
->flags
.beveled
= ((flag
== 0) ? 0 : 1);
515 if (tPtr
->view
->flags
.realized
) {
516 paintTextField(tPtr
);
520 void WMSetTextFieldSecure(WMTextField
* tPtr
, Bool flag
)
522 CHECK_CLASS(tPtr
, WC_TextField
);
524 tPtr
->flags
.secure
= ((flag
== 0) ? 0 : 1);
526 if (tPtr
->view
->flags
.realized
) {
527 paintTextField(tPtr
);
531 Bool
WMGetTextFieldEditable(WMTextField
* tPtr
)
533 CHECK_CLASS(tPtr
, WC_TextField
);
535 return tPtr
->flags
.enabled
;
538 void WMSetTextFieldEditable(WMTextField
* tPtr
, Bool flag
)
540 CHECK_CLASS(tPtr
, WC_TextField
);
542 tPtr
->flags
.enabled
= ((flag
== 0) ? 0 : 1);
544 if (tPtr
->view
->flags
.realized
) {
545 paintTextField(tPtr
);
549 void WMSelectTextFieldRange(WMTextField
* tPtr
, WMRange range
)
551 CHECK_CLASS(tPtr
, WC_TextField
);
553 if (tPtr
->flags
.enabled
) {
554 normalizeRange(tPtr
, &range
);
556 tPtr
->selection
= range
;
558 tPtr
->cursorPosition
= range
.position
+ range
.count
;
560 if (tPtr
->view
->flags
.realized
) {
561 paintTextField(tPtr
);
566 void WMSetTextFieldCursorPosition(WMTextField
* tPtr
, unsigned int position
)
568 CHECK_CLASS(tPtr
, WC_TextField
);
570 if (tPtr
->flags
.enabled
) {
571 if (position
> tPtr
->textLen
)
572 position
= tPtr
->textLen
;
574 tPtr
->cursorPosition
= position
;
575 if (tPtr
->view
->flags
.realized
) {
576 paintTextField(tPtr
);
581 unsigned WMGetTextFieldCursorPosition(WMTextField
*tPtr
)
583 CHECK_CLASS(tPtr
, WC_TextField
);
585 return tPtr
->cursorPosition
;
588 void WMSetTextFieldNextTextField(WMTextField
* tPtr
, WMTextField
* next
)
590 CHECK_CLASS(tPtr
, WC_TextField
);
592 if (tPtr
->view
->nextFocusChain
)
593 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
594 tPtr
->view
->nextFocusChain
= NULL
;
598 CHECK_CLASS(next
, WC_TextField
);
600 if (tPtr
->view
->nextFocusChain
)
601 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
602 if (next
->view
->prevFocusChain
)
603 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
605 tPtr
->view
->nextFocusChain
= next
->view
;
606 next
->view
->prevFocusChain
= tPtr
->view
;
609 void WMSetTextFieldPrevTextField(WMTextField
* tPtr
, WMTextField
* prev
)
611 CHECK_CLASS(tPtr
, WC_TextField
);
613 if (tPtr
->view
->prevFocusChain
)
614 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
615 tPtr
->view
->prevFocusChain
= NULL
;
619 CHECK_CLASS(prev
, WC_TextField
);
621 if (tPtr
->view
->prevFocusChain
)
622 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
623 if (prev
->view
->nextFocusChain
)
624 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
626 tPtr
->view
->prevFocusChain
= prev
->view
;
627 prev
->view
->nextFocusChain
= tPtr
->view
;
630 void WMSetTextFieldFont(WMTextField
* tPtr
, WMFont
* font
)
632 CHECK_CLASS(tPtr
, WC_TextField
);
635 WMReleaseFont(tPtr
->font
);
636 tPtr
->font
= WMRetainFont(font
);
638 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
640 if (tPtr
->view
->flags
.realized
) {
641 paintTextField(tPtr
);
645 WMFont
*WMGetTextFieldFont(WMTextField
* tPtr
)
650 static void didResizeTextField(W_ViewDelegate
* self
, WMView
* view
)
652 WMTextField
*tPtr
= (WMTextField
*) view
->self
;
654 tPtr
->offsetWidth
= WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
)) / 2, 1);
656 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2 * tPtr
->offsetWidth
/*+ 2 */ ;
659 static char *makeHiddenString(int length
)
661 char *data
= wmalloc(length
+ 1);
663 memset(data
, '*', length
);
669 static void paintCursor(TextField
* tPtr
)
672 WMScreen
*screen
= tPtr
->view
->screen
;
676 if (tPtr
->flags
.secure
)
677 text
= makeHiddenString(strlen(tPtr
->text
));
681 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]), tPtr
->cursorPosition
- tPtr
->viewPosition
);
683 switch (tPtr
->flags
.alignment
) {
685 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
686 if (textWidth
< tPtr
->usableWidth
)
687 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
689 cx
+= tPtr
->offsetWidth
+ 1;
692 cx
+= tPtr
->offsetWidth
+ 1;
697 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
698 if (textWidth
< tPtr
->usableWidth
)
699 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- textWidth
) / 2;
701 cx
+= tPtr
->offsetWidth
;
705 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
706 cx, tPtr->offsetWidth, 1,
707 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
708 printf("%d %d\n",cx,tPtr->cursorPosition);
711 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
712 cx
, tPtr
->offsetWidth
, cx
, tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
714 W_SetPreeditPositon(tPtr
->view
, cx
, 0);
716 if (tPtr
->flags
.secure
) {
721 static void drawRelief(WMView
* view
, Bool beveled
)
723 WMScreen
*scr
= view
->screen
;
724 Display
*dpy
= scr
->display
;
728 int width
= view
->size
.width
;
729 int height
= view
->size
.height
;
731 dgc
= WMColorGC(scr
->darkGray
);
734 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
- 1, height
- 1);
738 wgc
= WMColorGC(scr
->white
);
739 lgc
= WMColorGC(scr
->gray
);
742 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
- 1, 0);
743 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
- 2, 1);
745 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
- 2);
746 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
- 3);
749 XDrawLine(dpy
, view
->window
, wgc
, 0, height
- 1, width
- 1, height
- 1);
750 XDrawLine(dpy
, view
->window
, lgc
, 1, height
- 2, width
- 2, height
- 2);
752 XDrawLine(dpy
, view
->window
, wgc
, width
- 1, 0, width
- 1, height
- 1);
753 XDrawLine(dpy
, view
->window
, lgc
, width
- 2, 1, width
- 2, height
- 3);
756 static void paintTextField(TextField
* tPtr
)
758 W_Screen
*screen
= tPtr
->view
->screen
;
759 W_View
*view
= tPtr
->view
;
769 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
772 if (!tPtr
->flags
.bordered
) {
778 if (tPtr
->flags
.secure
) {
779 text
= makeHiddenString(strlen(tPtr
->text
));
784 totalWidth
= tPtr
->view
->size
.width
- 2 * bd
;
786 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
787 view
->size
.width
, view
->size
.height
, screen
->depth
);
788 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
789 0, 0, view
->size
.width
, view
->size
.height
);
790 /* this is quite dirty */
791 viewbuffer
.screen
= view
->screen
;
792 viewbuffer
.size
= view
->size
;
793 viewbuffer
.window
= drawbuffer
;
795 if (tPtr
->textLen
> 0) {
796 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]), tPtr
->textLen
- tPtr
->viewPosition
);
798 th
= WMFontHeight(tPtr
->font
);
800 ty
= tPtr
->offsetWidth
;
801 switch (tPtr
->flags
.alignment
) {
803 tx
= tPtr
->offsetWidth
+ 1;
804 if (tw
< tPtr
->usableWidth
)
805 XFillRectangle(screen
->display
, drawbuffer
,
806 WMColorGC(screen
->white
),
807 bd
+ tw
, bd
, totalWidth
- tw
, view
->size
.height
- 2 * bd
);
811 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
812 if (tw
< tPtr
->usableWidth
)
813 XClearArea(screen
->display
, view
->window
, bd
, bd
,
814 totalWidth
, view
->size
.height
- 2 * bd
, False
);
819 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
820 if (tw
< tPtr
->usableWidth
)
821 XClearArea(screen
->display
, view
->window
, bd
, bd
,
822 totalWidth
- tw
, view
->size
.height
- 2 * bd
, False
);
826 color
= tPtr
->flags
.enabled
? screen
->black
: screen
->darkGray
;
828 WMDrawImageString(screen
, drawbuffer
, color
, screen
->white
,
829 tPtr
->font
, tx
, ty
, &(text
[tPtr
->viewPosition
]),
830 tPtr
->textLen
- tPtr
->viewPosition
);
832 if (tPtr
->selection
.count
) {
835 count
= tPtr
->selection
.count
< 0
836 ? tPtr
->selection
.position
+ tPtr
->selection
.count
: tPtr
->selection
.position
;
837 count2
= abs(tPtr
->selection
.count
);
838 if (count
< tPtr
->viewPosition
) {
839 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
840 count
= tPtr
->viewPosition
;
843 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
, text
, count
)
844 - WMWidthOfString(tPtr
->font
, text
, tPtr
->viewPosition
);
846 WMDrawImageString(screen
, drawbuffer
, color
, screen
->gray
,
847 tPtr
->font
, rx
, ty
, &(text
[count
]), count2
);
850 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
851 bd
, bd
, totalWidth
, view
->size
.height
- 2 * bd
);
855 if (tPtr
->flags
.bordered
) {
856 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
859 if (tPtr
->flags
.secure
)
861 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
862 screen
->copyGC
, 0, 0, view
->size
.width
, view
->size
.height
, 0, 0);
863 XFreePixmap(screen
->display
, drawbuffer
);
866 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
872 static void blinkCursor(void *data
)
874 TextField
*tPtr
= (TextField
*) data
;
876 if (tPtr
->flags
.cursorOn
) {
877 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
, data
);
879 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
, data
);
882 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
886 static void handleEvents(XEvent
* event
, void *data
)
888 TextField
*tPtr
= (TextField
*) data
;
890 CHECK_CLASS(data
, WC_TextField
);
892 switch (event
->type
) {
894 W_FocusIC(tPtr
->view
);
895 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
)) != tPtr
->view
)
897 tPtr
->flags
.focused
= 1;
899 if (!tPtr
->timerID
) {
900 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
, tPtr
);
903 paintTextField(tPtr
);
905 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
907 tPtr
->flags
.notIllegalMovement
= 0;
911 W_UnFocusIC(tPtr
->view
);
912 tPtr
->flags
.focused
= 0;
915 WMDeleteTimerHandler(tPtr
->timerID
);
916 tPtr
->timerID
= NULL
;
919 paintTextField(tPtr
);
920 if (!tPtr
->flags
.notIllegalMovement
) {
921 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
922 (void *)WMIllegalTextMovement
);
927 if (event
->xexpose
.count
!= 0)
929 paintTextField(tPtr
);
933 destroyTextField(tPtr
);
938 static void handleTextFieldKeyPress(TextField
* tPtr
, XEvent
* event
)
942 char *textEvent
= NULL
;
944 int count
, refresh
= 0;
945 int control_pressed
= 0;
946 int cancelSelection
= 1;
947 Bool shifted
, controled
, modified
;
950 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count); */
951 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
)
954 shifted
= (event
->xkey
.state
& ShiftMask
? True
: False
);
955 controled
= (event
->xkey
.state
& ControlMask
? True
: False
);
956 modified
= shifted
|| controled
;
958 count
= W_LookupString(tPtr
->view
, &event
->xkey
, buffer
, 63, &ksym
, NULL
);
959 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
960 buffer
[count
] = '\0';
964 #ifdef XK_ISO_Left_Tab
965 case XK_ISO_Left_Tab
:
969 if (tPtr
->view
->prevFocusChain
) {
970 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
971 tPtr
->view
->prevFocusChain
);
972 tPtr
->flags
.notIllegalMovement
= 1;
974 data
= (void *)WMBacktabTextMovement
;
976 if (tPtr
->view
->nextFocusChain
) {
977 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
978 tPtr
->view
->nextFocusChain
);
979 tPtr
->flags
.notIllegalMovement
= 1;
981 data
= (void *)WMTabTextMovement
;
983 textEvent
= WMTextDidEndEditingNotification
;
993 data
= (void *)WMEscapeTextMovement
;
994 textEvent
= WMTextDidEndEditingNotification
;
1002 data
= (void *)WMReturnTextMovement
;
1003 textEvent
= WMTextDidEndEditingNotification
;
1009 case WM_EMACSKEY_LEFT
:
1010 if (!control_pressed
)
1019 if (tPtr
->cursorPosition
> 0) {
1023 i
= tPtr
->cursorPosition
;
1024 i
+= oneUTF8CharBackward(&tPtr
->text
[i
], i
);
1026 while (i
> 0 && tPtr
->text
[i
] != ' ')
1028 while (i
> 0 && tPtr
->text
[i
] == ' ')
1031 tPtr
->cursorPosition
= (i
> 0) ? i
+ 1 : 0;
1033 tPtr
->cursorPosition
= i
;
1035 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
1036 tPtr
->viewPosition
= tPtr
->cursorPosition
;
1042 cancelSelection
= 0;
1048 case WM_EMACSKEY_RIGHT
:
1049 if (!control_pressed
)
1058 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1062 i
= tPtr
->cursorPosition
;
1064 while (tPtr
->text
[i
] && tPtr
->text
[i
] != ' ')
1066 while (tPtr
->text
[i
] == ' ')
1069 i
+= oneUTF8CharForward(&tPtr
->text
[i
], tPtr
->textLen
- i
);
1071 tPtr
->cursorPosition
= i
;
1073 refresh
= incrToFit2(tPtr
);
1079 cancelSelection
= 0;
1085 case WM_EMACSKEY_HOME
:
1086 if (!control_pressed
)
1096 if (tPtr
->cursorPosition
> 0) {
1098 tPtr
->cursorPosition
= 0;
1099 if (tPtr
->viewPosition
> 0) {
1100 tPtr
->viewPosition
= 0;
1106 cancelSelection
= 0;
1112 case WM_EMACSKEY_END
:
1113 if (!control_pressed
)
1123 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1125 tPtr
->cursorPosition
= tPtr
->textLen
;
1126 tPtr
->viewPosition
= 0;
1128 refresh
= incrToFit(tPtr
);
1134 cancelSelection
= 0;
1140 case WM_EMACSKEY_BS
:
1141 if (!control_pressed
)
1148 if (tPtr
->selection
.count
) {
1149 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1150 data
= (void *)WMDeleteTextEvent
;
1151 textEvent
= WMTextDidChangeNotification
;
1152 } else if (tPtr
->cursorPosition
> 0) {
1153 int i
= oneUTF8CharBackward(&tPtr
->text
[tPtr
->cursorPosition
],
1154 tPtr
->cursorPosition
);
1156 range
.position
= tPtr
->cursorPosition
+ i
;
1158 WMDeleteTextFieldRange(tPtr
, range
);
1159 data
= (void *)WMDeleteTextEvent
;
1160 textEvent
= WMTextDidChangeNotification
;
1167 case WM_EMACSKEY_DEL
:
1168 if (!control_pressed
)
1178 if (tPtr
->selection
.count
) {
1179 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1180 data
= (void *)WMDeleteTextEvent
;
1181 textEvent
= WMTextDidChangeNotification
;
1182 } else if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1184 range
.position
= tPtr
->cursorPosition
;
1185 range
.count
= oneUTF8CharForward(&tPtr
->text
[tPtr
->cursorPosition
],
1186 tPtr
->textLen
- tPtr
->cursorPosition
);
1187 WMDeleteTextFieldRange(tPtr
, range
);
1188 data
= (void *)WMDeleteTextEvent
;
1189 textEvent
= WMTextDidChangeNotification
;
1199 if (count
> 0 && !iscntrl(buffer
[0])) {
1200 if (tPtr
->selection
.count
)
1201 WMDeleteTextFieldRange(tPtr
, tPtr
->selection
);
1202 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1203 data
= (void *)WMInsertTextEvent
;
1204 textEvent
= WMTextDidChangeNotification
;
1213 WMRelayToNextResponder(W_VIEW(tPtr
), event
);
1217 /* Do not allow text selection in secure text fields */
1218 if (cancelSelection
|| tPtr
->flags
.secure
) {
1219 lostSelection(tPtr
->view
, XA_PRIMARY
, NULL
);
1221 if (tPtr
->selection
.count
) {
1222 tPtr
->selection
.count
= 0;
1225 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1227 if (tPtr
->selection
.count
!= tPtr
->cursorPosition
- tPtr
->selection
.position
) {
1229 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1235 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1238 WMNotification
*notif
= WMCreateNotification(textEvent
, tPtr
, data
);
1240 if (tPtr
->delegate
) {
1241 if (textEvent
== WMTextDidBeginEditingNotification
&& tPtr
->delegate
->didBeginEditing
)
1242 (*tPtr
->delegate
->didBeginEditing
) (tPtr
->delegate
, notif
);
1244 else if (textEvent
== WMTextDidEndEditingNotification
&& tPtr
->delegate
->didEndEditing
)
1245 (*tPtr
->delegate
->didEndEditing
) (tPtr
->delegate
, notif
);
1247 else if (textEvent
== WMTextDidChangeNotification
&& tPtr
->delegate
->didChange
)
1248 (*tPtr
->delegate
->didChange
) (tPtr
->delegate
, notif
);
1251 WMPostNotification(notif
);
1252 WMReleaseNotification(notif
);
1256 paintTextField(tPtr
);
1258 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count); */
1261 static int pointToCursorPosition(TextField
* tPtr
, int x
)
1263 int a
, b
, pos
, prev
, tw
;
1265 if (tPtr
->flags
.bordered
)
1268 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1269 tPtr
->textLen
- tPtr
->viewPosition
) <= x
)
1270 return tPtr
->textLen
;
1272 a
= tPtr
->viewPosition
;
1275 /* we halve the text until we get into a 10 byte vicinity of x */
1276 while (b
- a
> 10) {
1278 pos
+= seekUTF8CharStart(&tPtr
->text
[pos
], pos
- a
);
1279 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]), pos
- tPtr
->viewPosition
);
1282 } else if (tw
< x
) {
1289 /* at this point x can be positioned on any glyph between 'a' and 'b-1'
1290 * inclusive, with the exception of the left border of the 'a' glyph and
1291 * the right border or the 'b-1' glyph
1293 * ( <--- range for x's position ---> )
1294 * a a+1 .......................... b-1 b
1298 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]), pos
- tPtr
->viewPosition
);
1301 } else if (pos
== b
) {
1305 pos
+= oneUTF8CharForward(&tPtr
->text
[pos
], b
- pos
);
1311 static void pasteText(WMView
* view
, Atom selection
, Atom target
, Time timestamp
, void *cdata
, WMData
* data
)
1313 TextField
*tPtr
= (TextField
*) view
->self
;
1316 tPtr
->flags
.waitingSelection
= 0;
1319 str
= (char *)WMDataBytes(data
);
1321 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1322 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, (void *)WMInsertTextEvent
);
1326 str
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1330 WMInsertTextFieldText(tPtr
, str
, tPtr
->cursorPosition
);
1332 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, (void *)WMInsertTextEvent
);
1337 static void handleTextFieldActionEvents(XEvent
* event
, void *data
)
1339 TextField
*tPtr
= (TextField
*) data
;
1340 static int move
= 0;
1341 static Time lastButtonReleasedEvent
= 0;
1342 static Time lastButtonReleasedEvent2
= 0;
1343 Display
*dpy
= event
->xany
.display
;
1345 CHECK_CLASS(data
, WC_TextField
);
1347 switch (event
->type
) {
1349 if (tPtr
->flags
.waitingSelection
) {
1352 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1353 handleTextFieldKeyPress(tPtr
, event
);
1354 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->invisibleCursor
);
1355 tPtr
->flags
.pointerGrabbed
= 1;
1361 if (tPtr
->flags
.pointerGrabbed
) {
1362 tPtr
->flags
.pointerGrabbed
= 0;
1363 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1365 if (tPtr
->flags
.waitingSelection
) {
1369 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1371 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
> tPtr
->usableWidth
) {
1372 if (WMWidthOfString(tPtr
->font
,
1373 &(tPtr
->text
[tPtr
->viewPosition
]),
1374 tPtr
->cursorPosition
- tPtr
->viewPosition
)
1375 > tPtr
->usableWidth
) {
1376 tPtr
->viewPosition
+= oneUTF8CharForward(&tPtr
->text
[tPtr
->viewPosition
],
1378 tPtr
->viewPosition
);
1380 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1382 tPtr
->viewPosition
+= oneUTF8CharBackward(&tPtr
->text
[tPtr
->viewPosition
],
1383 tPtr
->viewPosition
);
1386 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1388 /* Do not allow text selection in secure textfields */
1389 if (tPtr
->flags
.secure
) {
1390 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1393 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1396 paintTextField(tPtr
);
1402 if (tPtr
->flags
.pointerGrabbed
) {
1403 tPtr
->flags
.pointerGrabbed
= 0;
1404 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1408 if (tPtr
->flags
.waitingSelection
) {
1413 switch (tPtr
->flags
.alignment
) {
1416 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1417 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1418 WMSetFocusToWidget(tPtr
);
1420 if (tPtr
->flags
.focused
) {
1421 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1422 tPtr
->selection
.count
= 0;
1424 if (textWidth
< tPtr
->usableWidth
) {
1425 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1426 event
->xbutton
.x
- tPtr
->usableWidth
1429 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xbutton
.x
);
1431 paintTextField(tPtr
);
1435 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1436 WMSetFocusToWidget(tPtr
);
1438 if (tPtr
->flags
.focused
&& event
->xbutton
.button
== Button1
) {
1439 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
, event
->xbutton
.x
);
1440 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1441 tPtr
->selection
.count
= 0;
1442 paintTextField(tPtr
);
1444 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1448 if (!WMRequestSelection(tPtr
->view
, XA_PRIMARY
, XA_STRING
,
1449 event
->xbutton
.time
, pasteText
, NULL
)) {
1450 text
= XFetchBuffer(tPtr
->view
->screen
->display
, &n
, 0);
1454 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1456 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
1457 (void *)WMInsertTextEvent
);
1460 tPtr
->flags
.waitingSelection
= 1;
1470 if (tPtr
->flags
.pointerGrabbed
) {
1471 tPtr
->flags
.pointerGrabbed
= 0;
1472 XDefineCursor(dpy
, W_VIEW(tPtr
)->window
, W_VIEW(tPtr
)->screen
->textCursor
);
1474 if (tPtr
->flags
.waitingSelection
) {
1478 if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!= 0) {
1480 XRotateBuffers(dpy
, 1);
1482 count
= abs(tPtr
->selection
.count
);
1483 if (tPtr
->selection
.count
< 0)
1484 start
= tPtr
->selection
.position
- count
;
1486 start
= tPtr
->selection
.position
;
1488 XStoreBuffer(dpy
, &tPtr
->text
[start
], count
, 0);
1493 if (!tPtr
->flags
.secure
&&
1494 event
->xbutton
.time
- lastButtonReleasedEvent
<= WINGsConfiguration
.doubleClickDelay
) {
1496 if (event
->xbutton
.time
- lastButtonReleasedEvent2
<=
1497 2 * WINGsConfiguration
.doubleClickDelay
) {
1498 tPtr
->selection
.position
= 0;
1499 tPtr
->selection
.count
= tPtr
->textLen
;
1503 pos
= tPtr
->selection
.position
;
1504 cnt
= tPtr
->selection
.count
;
1507 if (txt
[pos
] == ' ' || txt
[pos
] == '\t')
1513 while (pos
+ cnt
< tPtr
->textLen
) {
1514 if (txt
[pos
+ cnt
] == ' ' || txt
[pos
+ cnt
] == '\t')
1518 tPtr
->selection
.position
= pos
;
1519 tPtr
->selection
.count
= cnt
;
1521 paintTextField(tPtr
);
1523 if (!tPtr
->flags
.ownsSelection
) {
1524 tPtr
->flags
.ownsSelection
=
1525 WMCreateSelectionHandler(tPtr
->view
,
1527 event
->xbutton
.time
, &selectionHandler
, NULL
);
1529 } else if (!tPtr
->flags
.secure
&& tPtr
->selection
.count
!= 0 && !tPtr
->flags
.ownsSelection
) {
1530 tPtr
->flags
.ownsSelection
=
1531 WMCreateSelectionHandler(tPtr
->view
,
1532 XA_PRIMARY
, event
->xbutton
.time
, &selectionHandler
, NULL
);
1535 lastButtonReleasedEvent2
= lastButtonReleasedEvent
;
1536 lastButtonReleasedEvent
= event
->xbutton
.time
;
1542 static void destroyTextField(TextField
* tPtr
)
1546 WMDeleteTimerHandler(tPtr
->timerID
);
1549 W_DestroyIC(tPtr
->view
);
1551 WMReleaseFont(tPtr
->font
);
1552 /*// use lostSelection() instead of WMDeleteSelectionHandler here? */
1553 WMDeleteSelectionHandler(tPtr
->view
, XA_PRIMARY
, CurrentTime
);
1554 WMRemoveNotificationObserver(tPtr
);