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 */
43 WMRange prevselection
;
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;
70 unsigned int notIllegalMovement
:1;
75 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
76 if ((T)->delegate && (T)->delegate->C)\
77 (*(T)->delegate->C)((T)->delegate,notif);\
78 WMPostNotification(notif);\
79 WMReleaseNotification(notif);}
82 #define MIN_TEXT_BUFFER 2
83 #define TEXT_BUFFER_INCR 8
86 #define WM_EMACSKEYMASK ControlMask
88 #define WM_EMACSKEY_LEFT XK_b
89 #define WM_EMACSKEY_RIGHT XK_f
90 #define WM_EMACSKEY_HOME XK_a
91 #define WM_EMACSKEY_END XK_e
92 #define WM_EMACSKEY_BS XK_h
93 #define WM_EMACSKEY_DEL XK_d
97 #define DEFAULT_WIDTH 60
98 #define DEFAULT_HEIGHT 20
99 #define DEFAULT_BORDERED True
100 #define DEFAULT_ALIGNMENT WALeft
104 static void destroyTextField(TextField
*tPtr
);
105 static void paintTextField(TextField
*tPtr
);
107 static void handleEvents(XEvent
*event
, void *data
);
108 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
109 static void resizeTextField();
111 struct W_ViewProcedureTable _TextFieldViewProcedures
= {
118 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
119 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
121 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
122 &((tPtr)->text[(start)]), (end) - (start) + 1))
126 memmv(char *dest
, char *src
, int size
)
131 for (i
=size
-1; i
>=0; i
--) {
134 } else if (dest
< src
) {
135 for (i
=0; i
<size
; i
++) {
143 incrToFit(TextField
*tPtr
)
145 int vp
= tPtr
->viewPosition
;
147 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
148 tPtr
->viewPosition
++;
150 return vp
!=tPtr
->viewPosition
;
155 incrToFit2(TextField
*tPtr
)
157 int vp
= tPtr
->viewPosition
;
158 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
159 >= tPtr
->usableWidth
)
160 tPtr
->viewPosition
++;
163 return vp
!=tPtr
->viewPosition
;
168 decrToFit(TextField
*tPtr
)
170 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
171 && tPtr
->viewPosition
>0)
172 tPtr
->viewPosition
--;
180 WMCreateTextField(WMWidget
*parent
)
184 tPtr
= wmalloc(sizeof(TextField
));
185 memset(tPtr
, 0, sizeof(TextField
));
187 tPtr
->widgetClass
= WC_TextField
;
189 tPtr
->view
= W_CreateView(W_VIEW(parent
));
194 tPtr
->view
->self
= tPtr
;
196 tPtr
->view
->attribFlags
|= CWCursor
;
197 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
199 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
201 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
204 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
206 tPtr
->flags
.enabled
= 1;
208 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
209 |FocusChangeMask
, handleEvents
, tPtr
);
211 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
213 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
215 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
216 tPtr
->flags
.beveled
= True
;
217 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
219 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
221 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
222 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
223 handleTextFieldActionEvents
, tPtr
);
225 tPtr
->flags
.cursorOn
= 1;
232 WMSetTextFieldDelegate(WMTextField
*tPtr
, WMTextFieldDelegate
*delegate
)
234 tPtr
->delegate
= delegate
;
239 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
243 CHECK_CLASS(tPtr
, WC_TextField
);
250 /* check if buffer will hold the text */
251 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
252 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
253 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
256 if (position
< 0 || position
>= tPtr
->textLen
) {
257 /* append the text at the end */
258 strcat(tPtr
->text
, text
);
262 tPtr
->textLen
+= len
;
263 tPtr
->cursorPosition
+= len
;
265 /* insert text at position */
266 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
267 tPtr
->textLen
-position
+1);
269 memcpy(&(tPtr
->text
[position
]), text
, len
);
271 tPtr
->textLen
+= len
;
272 if (position
>= tPtr
->cursorPosition
) {
273 tPtr
->cursorPosition
+= len
;
280 paintTextField(tPtr
);
282 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
283 (void*)WMInsertTextEvent
);
288 deleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
290 CHECK_CLASS(tPtr
, WC_TextField
);
292 if (range
.position
>= tPtr
->textLen
)
295 if (range
.count
< 1) {
296 if (range
.position
< 0)
298 tPtr
->text
[range
.position
] = 0;
299 tPtr
->textLen
= range
.position
;
301 tPtr
->cursorPosition
= 0;
302 tPtr
->viewPosition
= 0;
304 if (range
.position
+ range
.count
> tPtr
->textLen
)
305 range
.count
= tPtr
->textLen
- range
.position
;
306 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
307 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
308 tPtr
->textLen
-= range
.count
;
310 if (tPtr
->cursorPosition
> range
.position
)
311 tPtr
->cursorPosition
-= range
.count
;
316 paintTextField(tPtr
);
321 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
323 deleteTextFieldRange(tPtr
, range
);
324 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
325 (void*)WMDeleteTextEvent
);
331 WMGetTextFieldText(WMTextField
*tPtr
)
333 CHECK_CLASS(tPtr
, WC_TextField
);
335 return wstrdup(tPtr
->text
);
340 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
342 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
343 (!text
&& tPtr
->textLen
== 0))
350 tPtr
->textLen
= strlen(text
);
352 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
353 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
354 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
356 strcpy(tPtr
->text
, text
);
359 if (tPtr->textLen < tPtr->cursorPosition)
360 tPtr->cursorPosition = tPtr->textLen;
362 tPtr
->cursorPosition
= tPtr
->textLen
;
363 tPtr
->viewPosition
= 0;
364 tPtr
->selection
.count
= 0;
366 if (tPtr
->view
->flags
.realized
)
367 paintTextField(tPtr
);
369 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
,
370 (void*)WMSetTextEvent
);
375 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
377 /* TODO: update font change after field is mapped */
378 WMReleaseFont(tPtr
->font
);
379 tPtr
->font
= WMRetainFont(font
);
384 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
386 tPtr
->flags
.alignment
= alignment
;
387 if (alignment
!=WALeft
) {
388 wwarning("only left alignment is supported in textfields");
392 if (tPtr
->view
->flags
.realized
) {
393 paintTextField(tPtr
);
399 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
401 tPtr
->flags
.bordered
= bordered
;
403 if (tPtr
->view
->flags
.realized
) {
404 paintTextField(tPtr
);
410 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
412 tPtr
->flags
.beveled
= flag
;
414 if (tPtr
->view
->flags
.realized
) {
415 paintTextField(tPtr
);
422 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
424 tPtr
->flags
.secure
= flag
;
426 if (tPtr
->view
->flags
.realized
) {
427 paintTextField(tPtr
);
433 WMGetTextFieldEditable(WMTextField
*tPtr
)
435 return tPtr
->flags
.enabled
;
440 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
442 tPtr
->flags
.enabled
= flag
;
444 if (tPtr
->view
->flags
.realized
) {
445 paintTextField(tPtr
);
451 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
453 if (tPtr
->flags
.enabled
) {
454 if (range
.position
< 0) {
455 range
.count
+= range
.position
;
456 range
.count
= (range
.count
< 0) ? 0 : range
.count
;
458 } else if (range
.position
> tPtr
->textLen
) {
459 range
.position
= tPtr
->textLen
;
463 if (range
.position
+ range
.count
> tPtr
->textLen
)
464 range
.count
= tPtr
->textLen
- range
.position
;
466 tPtr
->prevselection
= tPtr
->selection
; /* check if this is needed */
468 tPtr
->selection
= range
;
470 if (tPtr
->view
->flags
.realized
) {
471 paintTextField(tPtr
);
478 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
480 if (tPtr
->flags
.enabled
) {
481 if (position
> tPtr
->textLen
)
482 position
= tPtr
->textLen
;
484 tPtr
->cursorPosition
= position
;
485 if (tPtr
->view
->flags
.realized
) {
486 paintTextField(tPtr
);
493 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
495 CHECK_CLASS(tPtr
, WC_TextField
);
497 if (tPtr
->view
->nextFocusChain
)
498 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
499 tPtr
->view
->nextFocusChain
= NULL
;
503 CHECK_CLASS(next
, WC_TextField
);
505 if (tPtr
->view
->nextFocusChain
)
506 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
507 if (next
->view
->prevFocusChain
)
508 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
510 tPtr
->view
->nextFocusChain
= next
->view
;
511 next
->view
->prevFocusChain
= tPtr
->view
;
516 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
518 CHECK_CLASS(tPtr
, WC_TextField
);
520 if (tPtr
->view
->prevFocusChain
)
521 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
522 tPtr
->view
->prevFocusChain
= NULL
;
526 CHECK_CLASS(prev
, WC_TextField
);
528 if (tPtr
->view
->prevFocusChain
)
529 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
530 if (prev
->view
->nextFocusChain
)
531 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
533 tPtr
->view
->prevFocusChain
= prev
->view
;
534 prev
->view
->nextFocusChain
= tPtr
->view
;
539 resizeTextField(WMTextField
*tPtr
, unsigned int width
, unsigned int height
)
541 W_ResizeView(tPtr
->view
, width
, height
);
544 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
546 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
551 makeHiddenString(int length
)
553 char *data
= wmalloc(length
+1);
555 memset(data
, '*', length
);
562 paintCursor(TextField
*tPtr
)
565 WMScreen
*screen
= tPtr
->view
->screen
;
569 if (tPtr
->flags
.secure
)
570 text
= makeHiddenString(strlen(tPtr
->text
));
574 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
575 tPtr
->cursorPosition
-tPtr
->viewPosition
);
577 switch (tPtr
->flags
.alignment
) {
579 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
580 if (textWidth
< tPtr
->usableWidth
)
581 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
583 cx
+= tPtr
->offsetWidth
+ 1;
586 cx
+= tPtr
->offsetWidth
+ 1;
591 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
592 if (textWidth
< tPtr
->usableWidth
)
593 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
595 cx
+= tPtr
->offsetWidth
;
599 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
600 cx, tPtr->offsetWidth, 1,
601 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
602 printf("%d %d\n",cx,tPtr->cursorPosition);
604 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
605 cx
, tPtr
->offsetWidth
, cx
,
606 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
608 if (tPtr
->flags
.secure
)
615 drawRelief(WMView
*view
, Bool beveled
)
617 WMScreen
*scr
= view
->screen
;
618 Display
*dpy
= scr
->display
;
622 int width
= view
->size
.width
;
623 int height
= view
->size
.height
;
625 dgc
= WMColorGC(scr
->darkGray
);
628 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
632 wgc
= WMColorGC(scr
->white
);
633 lgc
= WMColorGC(scr
->gray
);
636 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
637 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
639 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
640 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
643 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
644 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
646 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
647 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
652 paintTextField(TextField
*tPtr
)
654 W_Screen
*screen
= tPtr
->view
->screen
;
655 W_View
*view
= tPtr
->view
;
665 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
668 if (!tPtr
->flags
.bordered
) {
674 if (tPtr
->flags
.secure
) {
675 text
= makeHiddenString(strlen(tPtr
->text
));
680 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
682 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
683 view
->size
.width
, view
->size
.height
, screen
->depth
);
684 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
685 0,0, view
->size
.width
,view
->size
.height
);
686 /* this is quite dirty */
687 viewbuffer
.screen
= view
->screen
;
688 viewbuffer
.size
= view
->size
;
689 viewbuffer
.window
= drawbuffer
;
692 if (tPtr
->textLen
> 0) {
693 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
694 tPtr
->textLen
- tPtr
->viewPosition
);
696 th
= WMFontHeight(tPtr
->font
);
698 ty
= tPtr
->offsetWidth
;
699 switch (tPtr
->flags
.alignment
) {
701 tx
= tPtr
->offsetWidth
+ 1;
702 if (tw
< tPtr
->usableWidth
)
703 XFillRectangle(screen
->display
, drawbuffer
,
704 WMColorGC(screen
->white
),
705 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
709 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
710 if (tw
< tPtr
->usableWidth
)
711 XClearArea(screen
->display
, view
->window
, bd
, bd
,
712 totalWidth
, view
->size
.height
-2*bd
, False
);
717 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
718 if (tw
< tPtr
->usableWidth
)
719 XClearArea(screen
->display
, view
->window
, bd
, bd
,
720 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
724 if (!tPtr
->flags
.enabled
)
725 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
727 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
729 &(text
[tPtr
->viewPosition
]),
730 tPtr
->textLen
- tPtr
->viewPosition
);
732 if (tPtr
->selection
.count
) {
735 count
= tPtr
->selection
.count
< 0
736 ? tPtr
->selection
.position
+ tPtr
->selection
.count
737 : tPtr
->selection
.position
;
738 count2
= abs(tPtr
->selection
.count
);
739 if (count
< tPtr
->viewPosition
) {
740 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
741 count
= tPtr
->viewPosition
;
745 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
746 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
748 XSetBackground(screen
->display
, screen
->textFieldGC
,
749 screen
->gray
->color
.pixel
);
751 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
752 tPtr
->font
, rx
, ty
, &(text
[count
]),
755 XSetBackground(screen
->display
, screen
->textFieldGC
,
756 screen
->white
->color
.pixel
);
759 if (!tPtr
->flags
.enabled
)
760 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
762 XFillRectangle(screen
->display
, drawbuffer
,
763 WMColorGC(screen
->white
),
764 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
768 if (tPtr
->flags
.bordered
) {
769 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
772 if (tPtr
->flags
.secure
)
774 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
775 screen
->copyGC
, 0,0, view
->size
.width
,
776 view
->size
.height
,0,0);
777 XFreePixmap(screen
->display
, drawbuffer
);
780 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
788 blinkCursor(void *data
)
790 TextField
*tPtr
= (TextField
*)data
;
792 if (tPtr
->flags
.cursorOn
) {
793 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
796 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
800 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
806 handleEvents(XEvent
*event
, void *data
)
808 TextField
*tPtr
= (TextField
*)data
;
810 CHECK_CLASS(data
, WC_TextField
);
813 switch (event
->type
) {
815 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
817 tPtr
->flags
.focused
= 1;
819 if (!tPtr
->timerID
) {
820 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
824 paintTextField(tPtr
);
826 NOTIFY(tPtr
, didBeginEditing
, WMTextDidBeginEditingNotification
, NULL
);
828 tPtr
->flags
.notIllegalMovement
= 0;
832 tPtr
->flags
.focused
= 0;
835 WMDeleteTimerHandler(tPtr
->timerID
);
836 tPtr
->timerID
= NULL
;
839 paintTextField(tPtr
);
840 if (!tPtr
->flags
.notIllegalMovement
) {
841 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
842 (void*)WMIllegalTextMovement
);
847 if (event
->xexpose
.count
!=0)
849 paintTextField(tPtr
);
853 destroyTextField(tPtr
);
860 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
864 int count
, refresh
= 0;
865 int control_pressed
= 0;
867 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
) {
871 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
872 buffer
[count
] = '\0';
874 if (!(event
->xkey
.state
& ShiftMask
)) {
875 if (tPtr
->selection
.count
)
877 tPtr
->prevselection
= tPtr
->selection
;
878 tPtr
->selection
.position
= tPtr
->cursorPosition
;
879 tPtr
->selection
.count
= 0;
882 /* Be careful in any case in this switch statement, never to call
883 * to more than a function that can generate text change notifications.
884 * Only one text change notification should be sent in any case.
885 * Else hazardous things can happen.
886 * Maybe we need a better solution than the function wrapper to inform
887 * functions that change text in text fields, if they need to send a
888 * change notification or not. -Dan
892 case XK_ISO_Left_Tab
:
893 if (event
->xkey
.state
& ShiftMask
) {
894 if (tPtr
->view
->prevFocusChain
) {
895 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
896 tPtr
->view
->prevFocusChain
);
897 tPtr
->flags
.notIllegalMovement
= 1;
899 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
900 (void*)WMBacktabTextMovement
);
902 if (tPtr
->view
->nextFocusChain
) {
903 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
904 tPtr
->view
->nextFocusChain
);
905 tPtr
->flags
.notIllegalMovement
= 1;
907 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
908 (void*)WMTabTextMovement
);
913 NOTIFY(tPtr
, didEndEditing
, WMTextDidEndEditingNotification
,
914 (void*)WMReturnTextMovement
);
917 case WM_EMACSKEY_LEFT
:
918 if (!control_pressed
) {
923 if (tPtr
->cursorPosition
> 0) {
925 if (event
->xkey
.state
& ControlMask
) {
927 for (i
= tPtr
->cursorPosition
- 1; i
>= 0; i
--)
928 if (tPtr
->text
[i
] == ' ' || i
== 0) {
929 tPtr
->cursorPosition
= i
;
933 tPtr
->cursorPosition
--;
935 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
936 tPtr
->viewPosition
= tPtr
->cursorPosition
;
944 case WM_EMACSKEY_RIGHT
:
945 if (!control_pressed
) {
950 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
952 if (event
->xkey
.state
& ControlMask
) {
954 for (i
= tPtr
->cursorPosition
+ 1; i
<= tPtr
->textLen
; i
++)
955 if (tPtr
->text
[i
] == ' ' || i
== tPtr
->textLen
) {
956 tPtr
->cursorPosition
= i
;
960 tPtr
->cursorPosition
++;
962 while (WMWidthOfString(tPtr
->font
,
963 &(tPtr
->text
[tPtr
->viewPosition
]),
964 tPtr
->cursorPosition
-tPtr
->viewPosition
)
965 > tPtr
->usableWidth
) {
966 tPtr
->viewPosition
++;
974 case WM_EMACSKEY_HOME
:
975 if (!control_pressed
) {
980 if (tPtr
->cursorPosition
> 0) {
982 tPtr
->cursorPosition
= 0;
983 if (tPtr
->viewPosition
> 0) {
984 tPtr
->viewPosition
= 0;
992 case WM_EMACSKEY_END
:
993 if (!control_pressed
) {
998 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
1000 tPtr
->cursorPosition
= tPtr
->textLen
;
1001 tPtr
->viewPosition
= 0;
1002 while (WMWidthOfString(tPtr
->font
,
1003 &(tPtr
->text
[tPtr
->viewPosition
]),
1004 tPtr
->textLen
-tPtr
->viewPosition
)
1005 > tPtr
->usableWidth
) {
1006 tPtr
->viewPosition
++;
1014 case WM_EMACSKEY_BS
:
1015 if (!control_pressed
) {
1019 if (tPtr
->cursorPosition
> 0) {
1022 if (tPtr
->prevselection
.count
) {
1023 range
.position
= tPtr
->prevselection
.count
< 0
1024 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1025 : tPtr
->prevselection
.position
;
1027 range
.count
= abs(tPtr
->prevselection
.count
);
1029 range
.position
= tPtr
->cursorPosition
- 1;
1032 WMDeleteTextFieldRange(tPtr
, range
);
1036 case WM_EMACSKEY_DEL
:
1037 if (!control_pressed
) {
1042 if (tPtr
->cursorPosition
< tPtr
->textLen
|| tPtr
->prevselection
.count
) {
1045 if (tPtr
->prevselection
.count
) {
1046 range
.position
= tPtr
->prevselection
.count
< 0
1047 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1048 : tPtr
->prevselection
.position
;
1050 range
.count
= abs(tPtr
->prevselection
.count
);
1052 range
.position
= tPtr
->cursorPosition
;
1055 WMDeleteTextFieldRange(tPtr
, range
);
1061 if (count
> 0 && !iscntrl(buffer
[0])) {
1064 if (tPtr
->prevselection
.count
) {
1065 range
.position
= tPtr
->prevselection
.count
< 0
1066 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1067 : tPtr
->prevselection
.position
;
1069 range
.count
= abs(tPtr
->prevselection
.count
);
1071 range
.position
= tPtr
->cursorPosition
;
1074 if (tPtr
->prevselection
.count
)
1075 deleteTextFieldRange(tPtr
, range
);
1076 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1082 if (event
->xkey
.state
& ShiftMask
) {
1083 if (tPtr
->selection
.count
== 0)
1084 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1085 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1088 tPtr
->prevselection
.count
= 0;
1090 paintTextField(tPtr
);
1096 pointToCursorPosition(TextField
*tPtr
, int x
)
1101 if (tPtr
->flags
.bordered
)
1104 a
= tPtr
->viewPosition
;
1105 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1106 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1107 tPtr
->textLen
-tPtr
->viewPosition
) < x
)
1108 return tPtr
->textLen
;
1110 while (a
< b
&& b
-a
>1) {
1112 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1113 mid
- tPtr
->viewPosition
);
1126 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1128 TextField
*tPtr
= (TextField
*)data
;
1131 CHECK_CLASS(data
, WC_TextField
);
1133 switch (event
->type
) {
1135 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1136 handleTextFieldKeyPress(tPtr
, event
);
1137 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1138 W_VIEW(tPtr
)->window
, False
,
1139 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1140 GrabModeAsync
, GrabModeAsync
, None
,
1141 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1143 tPtr
->flags
.pointerGrabbed
= 1;
1149 if (tPtr
->flags
.pointerGrabbed
) {
1150 tPtr
->flags
.pointerGrabbed
= 0;
1151 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1154 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1156 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1157 tPtr
->usableWidth
) {
1158 if (WMWidthOfString(tPtr
->font
,
1159 &(tPtr
->text
[tPtr
->viewPosition
]),
1160 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1161 > tPtr
->usableWidth
) {
1162 tPtr
->viewPosition
++;
1164 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1166 tPtr
->viewPosition
--;
1169 if (!tPtr
->selection
.count
) {
1170 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1173 tPtr
->cursorPosition
=
1174 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1176 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1179 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1183 paintTextField(tPtr
);
1188 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1189 XA_PRIMARY
, None
, CurrentTime
);
1190 count
= tPtr
->selection
.count
< 0
1191 ? tPtr
->selection
.position
+ tPtr
->selection
.count
1192 : tPtr
->selection
.position
;
1193 XStoreBuffer(tPtr
->view
->screen
->display
,
1194 &tPtr
->text
[count
] , abs(tPtr
->selection
.count
), 0);
1199 if (tPtr
->flags
.pointerGrabbed
) {
1200 tPtr
->flags
.pointerGrabbed
= 0;
1201 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1206 switch (tPtr
->flags
.alignment
) {
1209 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1210 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1211 WMSetFocusToWidget(tPtr
);
1212 } else if (tPtr
->flags
.focused
) {
1213 tPtr
->selection
.count
= 0;
1215 if(textWidth
< tPtr
->usableWidth
){
1216 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1217 event
->xbutton
.x
- tPtr
->usableWidth
1220 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1223 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1225 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1228 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1231 paintTextField(tPtr
);
1235 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1236 WMSetFocusToWidget(tPtr
);
1237 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1239 paintTextField(tPtr
);
1240 } else if (tPtr
->flags
.focused
) {
1241 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1243 tPtr
->selection
.count
= 0;
1244 paintTextField(tPtr
);
1246 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1249 text
= W_GetTextSelection(tPtr
->view
->screen
,
1250 tPtr
->view
->screen
->clipboardAtom
);
1252 text
= W_GetTextSelection(tPtr
->view
->screen
,
1256 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1258 NOTIFY(tPtr
, didChange
, WMTextDidChangeNotification
, NULL
);
1266 if (tPtr
->flags
.pointerGrabbed
) {
1267 tPtr
->flags
.pointerGrabbed
= 0;
1268 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1278 destroyTextField(TextField
*tPtr
)
1282 WMDeleteTimerHandler(tPtr
->timerID
);
1285 WMReleaseFont(tPtr
->font
);