7 #include <X11/keysym.h>
12 #define CURSOR_BLINK_ON_DELAY 600
13 #define CURSOR_BLINK_OFF_DELAY 300
16 char *WMTextDidChangeNotification
= "WMTextDidChangeNotification";
17 char *WMTextDidBeginEditingNotification
= "WMTextDidBeginEditingNotification";
18 char *WMTextDidEndEditingNotification
= "WMTextDidEndEditingNotification";
21 typedef struct W_TextField
{
26 struct W_TextField
*nextField
; /* next textfield in the chain */
27 struct W_TextField
*prevField
;
31 int textLen
; /* size of text */
32 int bufferSize
; /* memory allocated for text */
34 int viewPosition
; /* position of text being shown */
36 int cursorPosition
; /* position of the insertion cursor */
39 short offsetWidth
; /* offset of text from border */
42 WMRange prevselection
;
47 WMHandlerID timerID
; /* for cursor blinking */
50 WMAlignment alignment
:2;
52 unsigned int bordered
:1;
54 unsigned int beveled
:1;
56 unsigned int enabled
:1;
58 unsigned int focused
:1;
60 unsigned int cursorOn
:1;
62 unsigned int secure
:1; /* password entry style */
64 unsigned int pointerGrabbed
:1;
67 unsigned int notIllegalMovement
:1;
72 #define MIN_TEXT_BUFFER 2
73 #define TEXT_BUFFER_INCR 8
76 #define WM_EMACSKEYMASK ControlMask
78 #define WM_EMACSKEY_LEFT XK_b
79 #define WM_EMACSKEY_RIGHT XK_f
80 #define WM_EMACSKEY_HOME XK_a
81 #define WM_EMACSKEY_END XK_e
82 #define WM_EMACSKEY_BS XK_h
83 #define WM_EMACSKEY_DEL XK_d
87 #define DEFAULT_WIDTH 60
88 #define DEFAULT_HEIGHT 20
89 #define DEFAULT_BORDERED True
90 #define DEFAULT_ALIGNMENT WALeft
94 static void destroyTextField(TextField
*tPtr
);
95 static void paintTextField(TextField
*tPtr
);
97 static void handleEvents(XEvent
*event
, void *data
);
98 static void handleTextFieldActionEvents(XEvent
*event
, void *data
);
99 static void resizeTextField();
101 struct W_ViewProcedureTable _TextFieldViewProcedures
= {
108 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
109 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
111 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
112 &((tPtr)->text[(start)]), (end) - (start) + 1))
116 memmv(char *dest
, char *src
, int size
)
121 for (i
=size
-1; i
>=0; i
--) {
124 } else if (dest
< src
) {
125 for (i
=0; i
<size
; i
++) {
133 incrToFit(TextField
*tPtr
)
135 int vp
= tPtr
->viewPosition
;
137 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
) > tPtr
->usableWidth
) {
138 tPtr
->viewPosition
++;
140 return vp
!=tPtr
->viewPosition
;
145 incrToFit2(TextField
*tPtr
)
147 int vp
= tPtr
->viewPosition
;
148 while (TEXT_WIDTH2(tPtr
, tPtr
->viewPosition
, tPtr
->cursorPosition
)
149 >= tPtr
->usableWidth
)
150 tPtr
->viewPosition
++;
153 return vp
!=tPtr
->viewPosition
;
158 decrToFit(TextField
*tPtr
)
160 while (TEXT_WIDTH(tPtr
, tPtr
->viewPosition
-1) < tPtr
->usableWidth
161 && tPtr
->viewPosition
>0)
162 tPtr
->viewPosition
--;
170 WMCreateTextField(WMWidget
*parent
)
174 tPtr
= wmalloc(sizeof(TextField
));
175 memset(tPtr
, 0, sizeof(TextField
));
177 tPtr
->widgetClass
= WC_TextField
;
179 tPtr
->view
= W_CreateView(W_VIEW(parent
));
184 tPtr
->view
->self
= tPtr
;
186 tPtr
->view
->attribFlags
|= CWCursor
;
187 tPtr
->view
->attribs
.cursor
= tPtr
->view
->screen
->textCursor
;
189 W_SetViewBackgroundColor(tPtr
->view
, tPtr
->view
->screen
->white
);
191 tPtr
->text
= wmalloc(MIN_TEXT_BUFFER
);
194 tPtr
->bufferSize
= MIN_TEXT_BUFFER
;
196 tPtr
->flags
.enabled
= 1;
198 WMCreateEventHandler(tPtr
->view
, ExposureMask
|StructureNotifyMask
199 |FocusChangeMask
, handleEvents
, tPtr
);
201 W_ResizeView(tPtr
->view
, DEFAULT_WIDTH
, DEFAULT_HEIGHT
);
203 tPtr
->font
= WMRetainFont(tPtr
->view
->screen
->normalFont
);
205 tPtr
->flags
.bordered
= DEFAULT_BORDERED
;
206 tPtr
->flags
.beveled
= True
;
207 tPtr
->flags
.alignment
= DEFAULT_ALIGNMENT
;
209 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
211 WMCreateEventHandler(tPtr
->view
, EnterWindowMask
|LeaveWindowMask
212 |ButtonReleaseMask
|ButtonPressMask
|KeyPressMask
|Button1MotionMask
,
213 handleTextFieldActionEvents
, tPtr
);
215 tPtr
->flags
.cursorOn
= 1;
222 WMInsertTextFieldText(WMTextField
*tPtr
, char *text
, int position
)
226 CHECK_CLASS(tPtr
, WC_TextField
);
233 /* check if buffer will hold the text */
234 if (len
+ tPtr
->textLen
>= tPtr
->bufferSize
) {
235 tPtr
->bufferSize
= tPtr
->textLen
+ len
+ TEXT_BUFFER_INCR
;
236 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
239 if (position
< 0 || position
>= tPtr
->textLen
) {
240 /* append the text at the end */
241 strcat(tPtr
->text
, text
);
245 tPtr
->textLen
+= len
;
246 tPtr
->cursorPosition
+= len
;
248 /* insert text at position */
249 memmv(&(tPtr
->text
[position
+len
]), &(tPtr
->text
[position
]),
250 tPtr
->textLen
-position
+1);
252 memcpy(&(tPtr
->text
[position
]), text
, len
);
254 tPtr
->textLen
+= len
;
255 if (position
>= tPtr
->cursorPosition
) {
256 tPtr
->cursorPosition
+= len
;
263 paintTextField(tPtr
);
265 WMPostNotificationName(WMTextDidChangeNotification
, tPtr
,
266 (void*)WMInsertTextEvent
);
271 deleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
273 CHECK_CLASS(tPtr
, WC_TextField
);
275 if (range
.position
>= tPtr
->textLen
)
278 if (range
.count
< 1) {
279 if (range
.position
< 0)
281 tPtr
->text
[range
.position
] = 0;
282 tPtr
->textLen
= range
.position
;
284 tPtr
->cursorPosition
= 0;
285 tPtr
->viewPosition
= 0;
287 if (range
.position
+ range
.count
> tPtr
->textLen
)
288 range
.count
= tPtr
->textLen
- range
.position
;
289 memmv(&(tPtr
->text
[range
.position
]), &(tPtr
->text
[range
.position
+range
.count
]),
290 tPtr
->textLen
- (range
.position
+range
.count
) + 1);
291 tPtr
->textLen
-= range
.count
;
293 if (tPtr
->cursorPosition
> range
.position
)
294 tPtr
->cursorPosition
-= range
.count
;
299 paintTextField(tPtr
);
304 WMDeleteTextFieldRange(WMTextField
*tPtr
, WMRange range
)
306 deleteTextFieldRange(tPtr
, range
);
307 WMPostNotificationName(WMTextDidChangeNotification
, tPtr
,
308 (void*)WMDeleteTextEvent
);
314 WMGetTextFieldText(WMTextField
*tPtr
)
316 CHECK_CLASS(tPtr
, WC_TextField
);
318 return wstrdup(tPtr
->text
);
323 WMSetTextFieldText(WMTextField
*tPtr
, char *text
)
325 if ((text
&& strcmp(tPtr
->text
, text
) == 0) ||
326 (!text
&& tPtr
->textLen
== 0))
333 tPtr
->textLen
= strlen(text
);
335 if (tPtr
->textLen
>= tPtr
->bufferSize
) {
336 tPtr
->bufferSize
= tPtr
->textLen
+ TEXT_BUFFER_INCR
;
337 tPtr
->text
= realloc(tPtr
->text
, tPtr
->bufferSize
);
339 strcpy(tPtr
->text
, text
);
342 if (tPtr->textLen < tPtr->cursorPosition)
343 tPtr->cursorPosition = tPtr->textLen;
345 tPtr
->cursorPosition
= tPtr
->textLen
;
346 tPtr
->viewPosition
= 0;
347 tPtr
->selection
.count
= 0;
349 if (tPtr
->view
->flags
.realized
)
350 paintTextField(tPtr
);
352 WMPostNotificationName(WMTextDidChangeNotification
, tPtr
,
353 (void*)WMSetTextEvent
);
358 WMSetTextFieldFont(WMTextField
*tPtr
, WMFont
*font
)
360 /* TODO: update font change after field is mapped */
361 WMReleaseFont(tPtr
->font
);
362 tPtr
->font
= WMRetainFont(font
);
366 WMSetTextFieldAlignment(WMTextField
*tPtr
, WMAlignment alignment
)
368 tPtr
->flags
.alignment
= alignment
;
369 if (alignment
!=WALeft
) {
370 wwarning("only left alignment is supported in textfields");
374 if (tPtr
->view
->flags
.realized
) {
375 paintTextField(tPtr
);
381 WMSetTextFieldBordered(WMTextField
*tPtr
, Bool bordered
)
383 tPtr
->flags
.bordered
= bordered
;
385 if (tPtr
->view
->flags
.realized
) {
386 paintTextField(tPtr
);
392 WMSetTextFieldBeveled(WMTextField
*tPtr
, Bool flag
)
394 tPtr
->flags
.beveled
= flag
;
396 if (tPtr
->view
->flags
.realized
) {
397 paintTextField(tPtr
);
404 WMSetTextFieldSecure(WMTextField
*tPtr
, Bool flag
)
406 tPtr
->flags
.secure
= flag
;
408 if (tPtr
->view
->flags
.realized
) {
409 paintTextField(tPtr
);
415 WMGetTextFieldEditable(WMTextField
*tPtr
)
417 return tPtr
->flags
.enabled
;
422 WMSetTextFieldEditable(WMTextField
*tPtr
, Bool flag
)
424 tPtr
->flags
.enabled
= flag
;
426 if (tPtr
->view
->flags
.realized
) {
427 paintTextField(tPtr
);
433 WMSelectTextFieldRange(WMTextField
*tPtr
, WMRange range
)
435 if (tPtr
->flags
.enabled
) {
436 if (range
.position
< 0) {
437 range
.count
+= range
.position
;
438 range
.count
= (range
.count
< 0) ? 0 : range
.count
;
440 } else if (range
.position
> tPtr
->textLen
) {
441 range
.position
= tPtr
->textLen
;
445 if (range
.position
+ range
.count
> tPtr
->textLen
)
446 range
.count
= tPtr
->textLen
- range
.position
;
448 tPtr
->prevselection
= tPtr
->selection
; /* check if this is needed */
450 tPtr
->selection
= range
;
452 if (tPtr
->view
->flags
.realized
) {
453 paintTextField(tPtr
);
460 WMSetTextFieldCursorPosition(WMTextField
*tPtr
, unsigned int position
)
462 if (tPtr
->flags
.enabled
) {
463 if (position
> tPtr
->textLen
)
464 position
= tPtr
->textLen
;
466 tPtr
->cursorPosition
= position
;
467 if (tPtr
->view
->flags
.realized
) {
468 paintTextField(tPtr
);
475 WMSetTextFieldNextTextField(WMTextField
*tPtr
, WMTextField
*next
)
477 CHECK_CLASS(tPtr
, WC_TextField
);
479 if (tPtr
->view
->nextFocusChain
)
480 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
481 tPtr
->view
->nextFocusChain
= NULL
;
485 CHECK_CLASS(next
, WC_TextField
);
487 if (tPtr
->view
->nextFocusChain
)
488 tPtr
->view
->nextFocusChain
->prevFocusChain
= NULL
;
489 if (next
->view
->prevFocusChain
)
490 next
->view
->prevFocusChain
->nextFocusChain
= NULL
;
492 tPtr
->view
->nextFocusChain
= next
->view
;
493 next
->view
->prevFocusChain
= tPtr
->view
;
498 WMSetTextFieldPrevTextField(WMTextField
*tPtr
, WMTextField
*prev
)
500 CHECK_CLASS(tPtr
, WC_TextField
);
502 if (tPtr
->view
->prevFocusChain
)
503 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
504 tPtr
->view
->prevFocusChain
= NULL
;
508 CHECK_CLASS(prev
, WC_TextField
);
510 if (tPtr
->view
->prevFocusChain
)
511 tPtr
->view
->prevFocusChain
->nextFocusChain
= NULL
;
512 if (prev
->view
->nextFocusChain
)
513 prev
->view
->nextFocusChain
->prevFocusChain
= NULL
;
515 tPtr
->view
->prevFocusChain
= prev
->view
;
516 prev
->view
->nextFocusChain
= tPtr
->view
;
521 resizeTextField(WMTextField
*tPtr
, unsigned int width
, unsigned int height
)
523 W_ResizeView(tPtr
->view
, width
, height
);
526 WMAX((tPtr
->view
->size
.height
- WMFontHeight(tPtr
->font
))/2, 1);
528 tPtr
->usableWidth
= tPtr
->view
->size
.width
- 2*tPtr
->offsetWidth
+ 2;
533 makeHiddenString(int length
)
535 char *data
= wmalloc(length
+1);
537 memset(data
, '*', length
);
544 paintCursor(TextField
*tPtr
)
547 WMScreen
*screen
= tPtr
->view
->screen
;
551 if (tPtr
->flags
.secure
)
552 text
= makeHiddenString(strlen(tPtr
->text
));
556 cx
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
557 tPtr
->cursorPosition
-tPtr
->viewPosition
);
559 switch (tPtr
->flags
.alignment
) {
561 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
562 if (textWidth
< tPtr
->usableWidth
)
563 cx
+= tPtr
->offsetWidth
+ tPtr
->usableWidth
- textWidth
+ 1;
565 cx
+= tPtr
->offsetWidth
+ 1;
568 cx
+= tPtr
->offsetWidth
+ 1;
573 textWidth
= WMWidthOfString(tPtr
->font
, text
, tPtr
->textLen
);
574 if (textWidth
< tPtr
->usableWidth
)
575 cx
+= tPtr
->offsetWidth
+ (tPtr
->usableWidth
-textWidth
)/2;
577 cx
+= tPtr
->offsetWidth
;
581 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
582 cx, tPtr->offsetWidth, 1,
583 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
584 printf("%d %d\n",cx,tPtr->cursorPosition);
586 XDrawLine(screen
->display
, tPtr
->view
->window
, screen
->xorGC
,
587 cx
, tPtr
->offsetWidth
, cx
,
588 tPtr
->view
->size
.height
- tPtr
->offsetWidth
- 1);
590 if (tPtr
->flags
.secure
)
597 drawRelief(WMView
*view
, Bool beveled
)
599 WMScreen
*scr
= view
->screen
;
600 Display
*dpy
= scr
->display
;
604 int width
= view
->size
.width
;
605 int height
= view
->size
.height
;
607 dgc
= WMColorGC(scr
->darkGray
);
610 XDrawRectangle(dpy
, view
->window
, dgc
, 0, 0, width
-1, height
-1);
614 wgc
= WMColorGC(scr
->white
);
615 lgc
= WMColorGC(scr
->gray
);
618 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, width
-1, 0);
619 XDrawLine(dpy
, view
->window
, dgc
, 0, 1, width
-2, 1);
621 XDrawLine(dpy
, view
->window
, dgc
, 0, 0, 0, height
-2);
622 XDrawLine(dpy
, view
->window
, dgc
, 1, 0, 1, height
-3);
625 XDrawLine(dpy
, view
->window
, wgc
, 0, height
-1, width
-1, height
-1);
626 XDrawLine(dpy
, view
->window
, lgc
, 1, height
-2, width
-2, height
-2);
628 XDrawLine(dpy
, view
->window
, wgc
, width
-1, 0, width
-1, height
-1);
629 XDrawLine(dpy
, view
->window
, lgc
, width
-2, 1, width
-2, height
-3);
634 paintTextField(TextField
*tPtr
)
636 W_Screen
*screen
= tPtr
->view
->screen
;
637 W_View
*view
= tPtr
->view
;
647 if (!view
->flags
.realized
|| !view
->flags
.mapped
)
650 if (!tPtr
->flags
.bordered
) {
656 if (tPtr
->flags
.secure
) {
657 text
= makeHiddenString(strlen(tPtr
->text
));
662 totalWidth
= tPtr
->view
->size
.width
- 2*bd
;
664 drawbuffer
= XCreatePixmap(screen
->display
, view
->window
,
665 view
->size
.width
, view
->size
.height
, screen
->depth
);
666 XFillRectangle(screen
->display
, drawbuffer
, WMColorGC(screen
->white
),
667 0,0, view
->size
.width
,view
->size
.height
);
668 /* this is quite dirty */
669 viewbuffer
.screen
= view
->screen
;
670 viewbuffer
.size
= view
->size
;
671 viewbuffer
.window
= drawbuffer
;
674 if (tPtr
->textLen
> 0) {
675 tw
= WMWidthOfString(tPtr
->font
, &(text
[tPtr
->viewPosition
]),
676 tPtr
->textLen
- tPtr
->viewPosition
);
678 th
= WMFontHeight(tPtr
->font
);
680 ty
= tPtr
->offsetWidth
;
681 switch (tPtr
->flags
.alignment
) {
683 tx
= tPtr
->offsetWidth
+ 1;
684 if (tw
< tPtr
->usableWidth
)
685 XFillRectangle(screen
->display
, drawbuffer
,
686 WMColorGC(screen
->white
),
687 bd
+tw
,bd
, totalWidth
-tw
,view
->size
.height
-2*bd
);
691 tx
= tPtr
->offsetWidth
+ (tPtr
->usableWidth
- tw
) / 2;
692 if (tw
< tPtr
->usableWidth
)
693 XClearArea(screen
->display
, view
->window
, bd
, bd
,
694 totalWidth
, view
->size
.height
-2*bd
, False
);
699 tx
= tPtr
->offsetWidth
+ tPtr
->usableWidth
- tw
- 1;
700 if (tw
< tPtr
->usableWidth
)
701 XClearArea(screen
->display
, view
->window
, bd
, bd
,
702 totalWidth
-tw
, view
->size
.height
-2*bd
, False
);
706 if (!tPtr
->flags
.enabled
)
707 WMSetColorInGC(screen
->darkGray
, screen
->textFieldGC
);
709 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
711 &(text
[tPtr
->viewPosition
]),
712 tPtr
->textLen
- tPtr
->viewPosition
);
714 if (tPtr
->selection
.count
) {
717 count
= tPtr
->selection
.count
< 0
718 ? tPtr
->selection
.position
+ tPtr
->selection
.count
719 : tPtr
->selection
.position
;
720 count2
= abs(tPtr
->selection
.count
);
721 if (count
< tPtr
->viewPosition
) {
722 count2
= abs(count2
- abs(tPtr
->viewPosition
- count
));
723 count
= tPtr
->viewPosition
;
727 rx
= tPtr
->offsetWidth
+ 1 + WMWidthOfString(tPtr
->font
,text
,count
)
728 - WMWidthOfString(tPtr
->font
,text
,tPtr
->viewPosition
);
730 XSetBackground(screen
->display
, screen
->textFieldGC
,
731 screen
->gray
->color
.pixel
);
733 WMDrawImageString(screen
, drawbuffer
, screen
->textFieldGC
,
734 tPtr
->font
, rx
, ty
, &(text
[count
]),
737 XSetBackground(screen
->display
, screen
->textFieldGC
,
738 screen
->white
->color
.pixel
);
741 if (!tPtr
->flags
.enabled
)
742 WMSetColorInGC(screen
->black
, screen
->textFieldGC
);
744 XFillRectangle(screen
->display
, drawbuffer
,
745 WMColorGC(screen
->white
),
746 bd
,bd
, totalWidth
,view
->size
.height
-2*bd
);
750 if (tPtr
->flags
.bordered
) {
751 drawRelief(&viewbuffer
, tPtr
->flags
.beveled
);
754 if (tPtr
->flags
.secure
)
756 XCopyArea(screen
->display
, drawbuffer
, view
->window
,
757 screen
->copyGC
, 0,0, view
->size
.width
,
758 view
->size
.height
,0,0);
759 XFreePixmap(screen
->display
, drawbuffer
);
762 if (tPtr
->flags
.focused
&& tPtr
->flags
.enabled
&& tPtr
->flags
.cursorOn
) {
770 blinkCursor(void *data
)
772 TextField
*tPtr
= (TextField
*)data
;
774 if (tPtr
->flags
.cursorOn
) {
775 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY
, blinkCursor
,
778 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
, blinkCursor
,
782 tPtr
->flags
.cursorOn
= !tPtr
->flags
.cursorOn
;
788 handleEvents(XEvent
*event
, void *data
)
790 TextField
*tPtr
= (TextField
*)data
;
792 CHECK_CLASS(data
, WC_TextField
);
795 switch (event
->type
) {
797 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr
->view
))!=tPtr
->view
)
799 tPtr
->flags
.focused
= 1;
801 if (!tPtr
->timerID
) {
802 tPtr
->timerID
= WMAddTimerHandler(CURSOR_BLINK_ON_DELAY
,
806 paintTextField(tPtr
);
808 WMPostNotificationName(WMTextDidBeginEditingNotification
, tPtr
, NULL
);
810 tPtr
->flags
.notIllegalMovement
= 0;
814 tPtr
->flags
.focused
= 0;
817 WMDeleteTimerHandler(tPtr
->timerID
);
818 tPtr
->timerID
= NULL
;
821 paintTextField(tPtr
);
822 if (!tPtr
->flags
.notIllegalMovement
) {
823 WMPostNotificationName(WMTextDidEndEditingNotification
, tPtr
,
824 (void*)WMIllegalTextMovement
);
829 if (event
->xexpose
.count
!=0)
831 paintTextField(tPtr
);
835 destroyTextField(tPtr
);
842 handleTextFieldKeyPress(TextField
*tPtr
, XEvent
*event
)
846 int count
, refresh
= 0;
847 int control_pressed
= 0;
849 if (((XKeyEvent
*) event
)->state
& WM_EMACSKEYMASK
) {
853 count
= XLookupString(&event
->xkey
, buffer
, 63, &ksym
, NULL
);
854 buffer
[count
] = '\0';
856 if (!(event
->xkey
.state
& ShiftMask
)) {
857 if (tPtr
->selection
.count
)
859 tPtr
->prevselection
= tPtr
->selection
;
860 tPtr
->selection
.position
= tPtr
->cursorPosition
;
861 tPtr
->selection
.count
= 0;
864 /* Be careful in any case in this switch statement, never to call
865 * to more than a function that can generate text change notifications.
866 * Only one text change notification should be sent in any case.
867 * Else hazardous things can happen.
868 * Maybe we need a better solution than the function wrapper to inform
869 * functions that change text in text fields, if they need to send a
870 * change notification or not. -Dan
874 if (event
->xkey
.state
& ShiftMask
) {
875 if (tPtr
->view
->prevFocusChain
) {
876 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
877 tPtr
->view
->prevFocusChain
);
878 tPtr
->flags
.notIllegalMovement
= 1;
880 WMPostNotificationName(WMTextDidEndEditingNotification
, tPtr
,
881 (void*)WMBacktabTextMovement
);
883 if (tPtr
->view
->nextFocusChain
) {
884 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr
->view
),
885 tPtr
->view
->nextFocusChain
);
886 tPtr
->flags
.notIllegalMovement
= 1;
888 WMPostNotificationName(WMTextDidEndEditingNotification
,
889 tPtr
, (void*)WMTabTextMovement
);
894 WMPostNotificationName(WMTextDidEndEditingNotification
, tPtr
,
895 (void*)WMReturnTextMovement
);
898 case WM_EMACSKEY_LEFT
:
899 if (!control_pressed
) {
904 if (tPtr
->cursorPosition
> 0) {
906 if (event
->xkey
.state
& ControlMask
) {
908 for (i
= tPtr
->cursorPosition
- 1; i
>= 0; i
--)
909 if (tPtr
->text
[i
] == ' ' || i
== 0) {
910 tPtr
->cursorPosition
= i
;
914 tPtr
->cursorPosition
--;
916 if (tPtr
->cursorPosition
< tPtr
->viewPosition
) {
917 tPtr
->viewPosition
= tPtr
->cursorPosition
;
925 case WM_EMACSKEY_RIGHT
:
926 if (!control_pressed
) {
931 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
933 if (event
->xkey
.state
& ControlMask
) {
935 for (i
= tPtr
->cursorPosition
+ 1; i
<= tPtr
->textLen
; i
++)
936 if (tPtr
->text
[i
] == ' ' || i
== tPtr
->textLen
) {
937 tPtr
->cursorPosition
= i
;
941 tPtr
->cursorPosition
++;
943 while (WMWidthOfString(tPtr
->font
,
944 &(tPtr
->text
[tPtr
->viewPosition
]),
945 tPtr
->cursorPosition
-tPtr
->viewPosition
)
946 > tPtr
->usableWidth
) {
947 tPtr
->viewPosition
++;
955 case WM_EMACSKEY_HOME
:
956 if (!control_pressed
) {
961 if (tPtr
->cursorPosition
> 0) {
963 tPtr
->cursorPosition
= 0;
964 if (tPtr
->viewPosition
> 0) {
965 tPtr
->viewPosition
= 0;
973 case WM_EMACSKEY_END
:
974 if (!control_pressed
) {
979 if (tPtr
->cursorPosition
< tPtr
->textLen
) {
981 tPtr
->cursorPosition
= tPtr
->textLen
;
982 tPtr
->viewPosition
= 0;
983 while (WMWidthOfString(tPtr
->font
,
984 &(tPtr
->text
[tPtr
->viewPosition
]),
985 tPtr
->textLen
-tPtr
->viewPosition
)
986 > tPtr
->usableWidth
) {
987 tPtr
->viewPosition
++;
996 if (!control_pressed
) {
1000 if (tPtr
->cursorPosition
> 0) {
1003 if (tPtr
->prevselection
.count
) {
1004 range
.position
= tPtr
->prevselection
.count
< 0
1005 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1006 : tPtr
->prevselection
.position
;
1008 range
.count
= abs(tPtr
->prevselection
.count
);
1010 range
.position
= tPtr
->cursorPosition
- 1;
1013 WMDeleteTextFieldRange(tPtr
, range
);
1017 case WM_EMACSKEY_DEL
:
1018 if (!control_pressed
) {
1023 if (tPtr
->cursorPosition
< tPtr
->textLen
|| tPtr
->prevselection
.count
) {
1026 if (tPtr
->prevselection
.count
) {
1027 range
.position
= tPtr
->prevselection
.count
< 0
1028 ? tPtr
->prevselection
.position
+ tPtr
->prevselection
.count
1029 : tPtr
->prevselection
.position
;
1031 range
.count
= abs(tPtr
->prevselection
.count
);
1033 range
.position
= tPtr
->cursorPosition
;
1036 WMDeleteTextFieldRange(tPtr
, range
);
1042 if (count
> 0 && !iscntrl(buffer
[0])) {
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 if (tPtr
->prevselection
.count
)
1056 deleteTextFieldRange(tPtr
, range
);
1057 WMInsertTextFieldText(tPtr
, buffer
, tPtr
->cursorPosition
);
1063 if (event
->xkey
.state
& ShiftMask
) {
1064 if (tPtr
->selection
.count
== 0)
1065 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1066 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1069 tPtr
->prevselection
.count
= 0;
1071 paintTextField(tPtr
);
1077 pointToCursorPosition(TextField
*tPtr
, int x
)
1082 if (tPtr
->flags
.bordered
)
1085 a
= tPtr
->viewPosition
;
1086 b
= tPtr
->viewPosition
+ tPtr
->textLen
;
1087 if (WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1088 tPtr
->textLen
-tPtr
->viewPosition
) < x
)
1089 return tPtr
->textLen
;
1091 while (a
< b
&& b
-a
>1) {
1093 tw
= WMWidthOfString(tPtr
->font
, &(tPtr
->text
[tPtr
->viewPosition
]),
1094 mid
- tPtr
->viewPosition
);
1107 handleTextFieldActionEvents(XEvent
*event
, void *data
)
1109 TextField
*tPtr
= (TextField
*)data
;
1112 CHECK_CLASS(data
, WC_TextField
);
1114 switch (event
->type
) {
1116 if (tPtr
->flags
.enabled
&& tPtr
->flags
.focused
) {
1117 handleTextFieldKeyPress(tPtr
, event
);
1118 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
),
1119 W_VIEW(tPtr
)->window
, False
,
1120 PointerMotionMask
|ButtonPressMask
|ButtonReleaseMask
,
1121 GrabModeAsync
, GrabModeAsync
, None
,
1122 W_VIEW(tPtr
)->screen
->invisibleCursor
,
1124 tPtr
->flags
.pointerGrabbed
= 1;
1130 if (tPtr
->flags
.pointerGrabbed
) {
1131 tPtr
->flags
.pointerGrabbed
= 0;
1132 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1135 if (tPtr
->flags
.enabled
&& (event
->xmotion
.state
& Button1Mask
)) {
1137 if (tPtr
->viewPosition
< tPtr
->textLen
&& event
->xmotion
.x
>
1138 tPtr
->usableWidth
) {
1139 if (WMWidthOfString(tPtr
->font
,
1140 &(tPtr
->text
[tPtr
->viewPosition
]),
1141 tPtr
->cursorPosition
-tPtr
->viewPosition
)
1142 > tPtr
->usableWidth
) {
1143 tPtr
->viewPosition
++;
1145 } else if (tPtr
->viewPosition
> 0 && event
->xmotion
.x
< 0) {
1147 tPtr
->viewPosition
--;
1150 if (!tPtr
->selection
.count
) {
1151 tPtr
->selection
.position
= tPtr
->cursorPosition
;
1154 tPtr
->cursorPosition
=
1155 pointToCursorPosition(tPtr
, event
->xmotion
.x
);
1157 tPtr
->selection
.count
= tPtr
->cursorPosition
- tPtr
->selection
.position
;
1160 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1164 paintTextField(tPtr
);
1169 XSetSelectionOwner(tPtr
->view
->screen
->display
,
1170 XA_PRIMARY
, None
, CurrentTime
);
1171 count
= tPtr
->selection
.count
< 0
1172 ? tPtr
->selection
.position
+ tPtr
->selection
.count
1173 : tPtr
->selection
.position
;
1174 XStoreBuffer(tPtr
->view
->screen
->display
,
1175 &tPtr
->text
[count
] , abs(tPtr
->selection
.count
), 0);
1180 if (tPtr
->flags
.pointerGrabbed
) {
1181 tPtr
->flags
.pointerGrabbed
= 0;
1182 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1186 switch (tPtr
->flags
.alignment
) {
1189 textWidth
= WMWidthOfString(tPtr
->font
, tPtr
->text
, tPtr
->textLen
);
1190 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1191 WMSetFocusToWidget(tPtr
);
1192 } else if (tPtr
->flags
.focused
) {
1193 tPtr
->selection
.count
= 0;
1195 if(textWidth
< tPtr
->usableWidth
){
1196 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1197 event
->xbutton
.x
- tPtr
->usableWidth
1200 else tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1203 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1205 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1208 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1211 paintTextField(tPtr
);
1215 if (tPtr
->flags
.enabled
&& !tPtr
->flags
.focused
) {
1216 WMSetFocusToWidget(tPtr
);
1217 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1219 paintTextField(tPtr
);
1220 } else if (tPtr
->flags
.focused
) {
1221 tPtr
->cursorPosition
= pointToCursorPosition(tPtr
,
1223 tPtr
->selection
.count
= 0;
1224 paintTextField(tPtr
);
1226 if (event
->xbutton
.button
== Button2
&& tPtr
->flags
.enabled
) {
1229 text
= W_GetTextSelection(tPtr
->view
->screen
,
1230 tPtr
->view
->screen
->clipboardAtom
);
1232 text
= W_GetTextSelection(tPtr
->view
->screen
, XA_CUT_BUFFER0
);
1235 WMInsertTextFieldText(tPtr
, text
, tPtr
->cursorPosition
);
1237 WMPostNotificationName(WMTextDidChangeNotification
, tPtr
, NULL
);
1245 if (tPtr
->flags
.pointerGrabbed
) {
1246 tPtr
->flags
.pointerGrabbed
= 0;
1247 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr
)->screen
), CurrentTime
);
1257 destroyTextField(TextField
*tPtr
)
1261 WMDeleteTimerHandler(tPtr
->timerID
);
1264 WMReleaseFont(tPtr
->font
);