Update for 0.51.0
[wmaker-crm.git] / WINGs / wtextfield.c
blob5d980e476fc52a0eb803502ee64ea99ec733a83a
5 #include "WINGsP.h"
7 #include <X11/keysym.h>
8 #include <X11/Xatom.h>
10 #include <ctype.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 {
22 W_Class widgetClass;
23 W_View *view;
25 struct W_TextField *nextField; /* next textfield in the chain */
26 struct W_TextField *prevField;
28 char *text;
29 int textLen; /* size of text */
30 int bufferSize; /* memory allocated for text */
32 int viewPosition; /* position of text being shown */
34 int cursorPosition; /* position of the insertion cursor */
36 short usableWidth;
37 short offsetWidth; /* offset of text from border */
39 WMRange selection;
40 WMRange prevselection;
42 #if 0
43 WMHandlerID timerID; /* for cursor blinking */
44 #endif
45 struct {
46 WMAlignment alignment:2;
48 unsigned int bordered:1;
50 unsigned int enabled:1;
52 unsigned int focused:1;
54 unsigned int cursorOn:1;
56 unsigned int secure:1; /* password entry style */
58 /**/
59 unsigned int notIllegalMovement:1;
60 } flags;
61 } TextField;
64 #define MIN_TEXT_BUFFER 2
65 #define TEXT_BUFFER_INCR 8
68 #define WM_EMACSKEYMASK ControlMask
70 #define WM_EMACSKEY_LEFT XK_b
71 #define WM_EMACSKEY_RIGHT XK_f
72 #define WM_EMACSKEY_HOME XK_a
73 #define WM_EMACSKEY_END XK_e
74 #define WM_EMACSKEY_BS XK_h
75 #define WM_EMACSKEY_DEL XK_d
79 #define DEFAULT_WIDTH 60
80 #define DEFAULT_HEIGHT 20
81 #define DEFAULT_BORDERED True
82 #define DEFAULT_ALIGNMENT WALeft
86 static void destroyTextField(TextField *tPtr);
87 static void paintTextField(TextField *tPtr);
89 static void handleEvents(XEvent *event, void *data);
90 static void handleTextFieldActionEvents(XEvent *event, void *data);
91 static void resizeTextField();
93 struct W_ViewProcedureTable _TextFieldViewProcedures = {
94 NULL,
95 resizeTextField,
96 NULL
100 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->view->screen->normalFont, \
101 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
103 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->view->screen->normalFont, \
104 &((tPtr)->text[(start)]), (end) - (start) + 1))
107 static void
108 memmv(char *dest, char *src, int size)
110 int i;
112 if (dest > src) {
113 for (i=size-1; i>=0; i--) {
114 dest[i] = src[i];
116 } else if (dest < src) {
117 for (i=0; i<size; i++) {
118 dest[i] = src[i];
124 static int
125 incrToFit(TextField *tPtr)
127 int vp = tPtr->viewPosition;
129 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
130 tPtr->viewPosition++;
132 return vp!=tPtr->viewPosition;
136 static int
137 incrToFit2(TextField *tPtr)
139 int vp = tPtr->viewPosition;
140 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
141 >= tPtr->usableWidth)
142 tPtr->viewPosition++;
145 return vp!=tPtr->viewPosition;
149 static void
150 decrToFit(TextField *tPtr)
152 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
153 && tPtr->viewPosition>0)
154 tPtr->viewPosition--;
157 #undef TEXT_WIDTH
158 #undef TEXT_WIDTH2
161 WMTextField*
162 WMCreateTextField(WMWidget *parent)
164 TextField *tPtr;
167 tPtr = wmalloc(sizeof(TextField));
168 memset(tPtr, 0, sizeof(TextField));
170 tPtr->widgetClass = WC_TextField;
172 tPtr->view = W_CreateView(W_VIEW(parent));
173 if (!tPtr->view) {
174 free(tPtr);
175 return NULL;
177 tPtr->view->self = tPtr;
179 tPtr->view->attribFlags |= CWCursor;
180 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
182 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
184 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
185 tPtr->text[0] = 0;
186 tPtr->textLen = 0;
187 tPtr->bufferSize = MIN_TEXT_BUFFER;
189 tPtr->flags.enabled = 1;
191 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
192 |FocusChangeMask, handleEvents, tPtr);
194 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
195 WMSetTextFieldBordered(tPtr, DEFAULT_BORDERED);
196 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
197 tPtr->offsetWidth = (tPtr->view->size.height
198 - WMFontHeight(tPtr->view->screen->normalFont))/2;
200 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
201 |ButtonPressMask|KeyPressMask|Button1MotionMask,
202 handleTextFieldActionEvents, tPtr);
204 tPtr->flags.cursorOn = 1;
206 return tPtr;
210 void
211 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
213 int len;
215 CHECK_CLASS(tPtr, WC_TextField);
217 if (!text)
218 return;
220 len = strlen(text);
222 /* check if buffer will hold the text */
223 if (len + tPtr->textLen >= tPtr->bufferSize) {
224 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
225 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
228 if (position < 0 || position >= tPtr->textLen) {
229 /* append the text at the end */
230 strcat(tPtr->text, text);
232 incrToFit(tPtr);
234 tPtr->textLen += len;
235 tPtr->cursorPosition += len;
236 } else {
237 /* insert text at position */
238 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
239 tPtr->textLen-position+1);
241 memcpy(&(tPtr->text[position]), text, len);
243 tPtr->textLen += len;
244 if (position >= tPtr->cursorPosition) {
245 tPtr->cursorPosition += len;
246 incrToFit2(tPtr);
247 } else {
248 incrToFit(tPtr);
252 paintTextField(tPtr);
254 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
258 void
259 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
261 CHECK_CLASS(tPtr, WC_TextField);
263 if (range.position >= tPtr->textLen)
264 return;
266 if (range.count < 1) {
267 if (range.position < 0)
268 range.position = 0;
269 tPtr->text[range.position] = 0;
270 tPtr->textLen = range.position;
272 tPtr->cursorPosition = 0;
273 tPtr->viewPosition = 0;
274 } else {
275 if (range.position + range.count > tPtr->textLen)
276 range.count = tPtr->textLen - range.position;
277 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
278 tPtr->textLen - (range.position+range.count) + 1);
279 tPtr->textLen -= range.count;
281 if (tPtr->cursorPosition > range.position)
282 tPtr->cursorPosition -= range.count;
284 decrToFit(tPtr);
287 paintTextField(tPtr);
289 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
294 char*
295 WMGetTextFieldText(WMTextField *tPtr)
297 CHECK_CLASS(tPtr, WC_TextField);
299 return wstrdup(tPtr->text);
303 void
304 WMSetTextFieldText(WMTextField *tPtr, char *text)
306 if (text==NULL) {
307 tPtr->text[0] = 0;
308 tPtr->textLen = 0;
309 } else {
310 tPtr->textLen = strlen(text);
312 if (tPtr->textLen >= tPtr->bufferSize) {
313 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
314 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
316 strcpy(tPtr->text, text);
319 if (tPtr->textLen < tPtr->cursorPosition)
320 tPtr->cursorPosition = tPtr->textLen;
322 tPtr->cursorPosition = 0;
323 tPtr->viewPosition = 0;
325 if (tPtr->view->flags.realized)
326 paintTextField(tPtr);
328 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
332 void
333 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
335 tPtr->flags.alignment = alignment;
336 if (alignment!=WALeft) {
337 wwarning("only left alignment is supported in textfields");
338 return;
341 if (tPtr->view->flags.realized) {
342 paintTextField(tPtr);
347 void
348 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
350 tPtr->flags.bordered = bordered;
352 if (tPtr->view->flags.realized) {
353 paintTextField(tPtr);
359 void
360 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
362 tPtr->flags.secure = flag;
364 if (tPtr->view->flags.realized) {
365 paintTextField(tPtr);
370 void
371 WMSetTextFieldEnabled(WMTextField *tPtr, Bool flag)
373 tPtr->flags.enabled = flag;
375 if (tPtr->view->flags.realized) {
376 paintTextField(tPtr);
381 static void
382 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
384 W_ResizeView(tPtr->view, width, height);
386 tPtr->offsetWidth = (tPtr->view->size.height
387 - WMFontHeight(tPtr->view->screen->normalFont))/2;
389 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth;
393 static void
394 paintCursor(TextField *tPtr)
396 int cx;
397 WMScreen *screen = tPtr->view->screen;
398 int textWidth;
400 cx = WMWidthOfString(screen->normalFont,
401 &(tPtr->text[tPtr->viewPosition]),
402 tPtr->cursorPosition-tPtr->viewPosition);
404 switch (tPtr->flags.alignment) {
405 case WARight:
406 textWidth = WMWidthOfString(screen->normalFont, tPtr->text,
407 tPtr->textLen);
408 if (textWidth < tPtr->usableWidth)
409 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth;
410 else
411 cx += tPtr->offsetWidth;
412 break;
413 case WALeft:
414 cx += tPtr->offsetWidth;
415 break;
416 case WAJustified:
417 /* not supported */
418 case WACenter:
419 textWidth = WMWidthOfString(screen->normalFont, tPtr->text,
420 tPtr->textLen);
421 if (textWidth < tPtr->usableWidth)
422 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
423 else
424 cx += tPtr->offsetWidth;
425 break;
428 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
429 cx, tPtr->offsetWidth, 1,
430 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
432 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
433 cx, tPtr->offsetWidth, cx,
434 tPtr->view->size.height - tPtr->offsetWidth - 1);
439 static void
440 drawRelief(WMView *view)
442 WMScreen *scr = view->screen;
443 Display *dpy = scr->display;
444 GC wgc;
445 GC lgc;
446 GC dgc;
447 int width = view->size.width;
448 int height = view->size.height;
450 wgc = W_GC(scr->white);
451 dgc = W_GC(scr->darkGray);
452 lgc = W_GC(scr->gray);
454 /* top left */
455 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
456 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
458 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
459 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
461 /* bottom right */
462 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
463 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
465 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
466 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
470 static void
471 paintTextField(TextField *tPtr)
473 W_Screen *screen = tPtr->view->screen;
474 W_View *view = tPtr->view;
475 int tx, ty, tw, th;
476 int rx;
477 int bd;
478 int totalWidth;
481 if (!view->flags.realized || !view->flags.mapped)
482 return;
484 if (!tPtr->flags.bordered) {
485 bd = 0;
486 } else {
487 bd = 2;
490 totalWidth = tPtr->view->size.width - 2*bd;
492 if (tPtr->textLen > 0) {
493 tw = WMWidthOfString(screen->normalFont,
494 &(tPtr->text[tPtr->viewPosition]),
495 tPtr->textLen - tPtr->viewPosition);
497 th = WMFontHeight(screen->normalFont);
499 ty = tPtr->offsetWidth;
500 switch (tPtr->flags.alignment) {
501 case WALeft:
502 tx = tPtr->offsetWidth;
503 if (tw < tPtr->usableWidth)
504 XClearArea(screen->display, view->window, bd+tw, bd,
505 totalWidth-tw, view->size.height-2*bd,
506 False);
507 break;
509 case WACenter:
510 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
511 if (tw < tPtr->usableWidth)
512 XClearArea(screen->display, view->window, bd, bd,
513 totalWidth, view->size.height-2*bd, False);
514 break;
516 default:
517 case WARight:
518 tx = tPtr->offsetWidth + tPtr->usableWidth - tw;
519 if (tw < tPtr->usableWidth)
520 XClearArea(screen->display, view->window, bd, bd,
521 totalWidth-tw, view->size.height-2*bd, False);
522 break;
525 if (!tPtr->flags.secure) {
526 if (!tPtr->flags.enabled)
527 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
529 WMDrawImageString(screen, view->window, screen->textFieldGC,
530 screen->normalFont, tx, ty,
531 &(tPtr->text[tPtr->viewPosition]),
532 tPtr->textLen - tPtr->viewPosition);
534 if (tPtr->selection.count) {
535 int count;
537 count = tPtr->selection.count < 0
538 ? tPtr->selection.position + tPtr->selection.count
539 : tPtr->selection.position;
541 rx = tx + WMWidthOfString(screen->normalFont,
542 &(tPtr->text[tPtr->viewPosition]),
543 count);
545 XSetBackground(screen->display, screen->textFieldGC,
546 screen->gray->color.pixel);
548 WMDrawImageString(screen, view->window, screen->textFieldGC,
549 screen->normalFont, rx, ty,
550 &(tPtr->text[count]),
551 abs(tPtr->selection.count));
553 XSetBackground(screen->display, screen->textFieldGC,
554 screen->white->color.pixel);
557 if (!tPtr->flags.enabled)
558 WMSetColorInGC(screen->black, screen->textFieldGC);
560 } else {
561 XClearArea(screen->display, view->window, bd, bd, totalWidth,
562 view->size.height - 2*bd, False);
565 /* draw cursor */
566 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn
567 && !tPtr->flags.secure) {
568 paintCursor(tPtr);
571 /* draw relief */
572 if (tPtr->flags.bordered) {
573 drawRelief(view);
578 #if 0
579 static void
580 blinkCursor(void *data)
582 TextField *tPtr = (TextField*)data;
584 if (tPtr->flags.cursorOn) {
585 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
586 data);
587 } else {
588 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
589 data);
591 paintCursor(tPtr);
592 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
594 #endif
597 static void
598 handleEvents(XEvent *event, void *data)
600 TextField *tPtr = (TextField*)data;
602 CHECK_CLASS(data, WC_TextField);
605 switch (event->type) {
606 case FocusIn:
607 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
608 return;
609 tPtr->flags.focused = 1;
610 #if 0
611 if (!tPtr->timerID) {
612 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
613 blinkCursor, tPtr);
615 #endif
616 paintTextField(tPtr);
618 WMPostNotificationName(WMTextDidBeginEditingNotification, tPtr, NULL);
620 tPtr->flags.notIllegalMovement = 0;
621 break;
623 case FocusOut:
624 tPtr->flags.focused = 0;
625 #if 0
626 if (tPtr->timerID)
627 WMDeleteTimerHandler(tPtr->timerID);
628 tPtr->timerID = NULL;
629 #endif
631 paintTextField(tPtr);
632 if (!tPtr->flags.notIllegalMovement) {
633 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
634 (void*)WMIllegalTextMovement);
636 break;
638 case Expose:
639 if (event->xexpose.count!=0)
640 break;
641 paintTextField(tPtr);
642 break;
644 case DestroyNotify:
645 destroyTextField(tPtr);
646 break;
651 static void
652 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
654 char buffer[64];
655 KeySym ksym;
656 int count, refresh = 0;
657 int control_pressed = 0;
658 int changed;
659 WMScreen *scr = tPtr->view->screen;
661 changed = 0;
663 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
664 control_pressed = 1;
667 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
668 buffer[count] = '\0';
670 if (!event->xkey.state & ShiftMask) {
671 if (tPtr->selection.count)
672 refresh = 1;
673 tPtr->prevselection = tPtr->selection;
674 tPtr->selection.position = tPtr->cursorPosition;
675 tPtr->selection.count = 0;
678 switch (ksym) {
679 case XK_Tab:
680 if (event->xkey.state & ShiftMask) {
681 if (tPtr->view->prevFocusChain) {
682 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
683 tPtr->view->prevFocusChain);
684 tPtr->flags.notIllegalMovement = 1;
686 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
687 (void*)WMBacktabTextMovement);
688 } else {
689 if (tPtr->view->nextFocusChain) {
690 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
691 tPtr->view->nextFocusChain);
692 tPtr->flags.notIllegalMovement = 1;
694 WMPostNotificationName(WMTextDidEndEditingNotification,
695 tPtr, (void*)WMTabTextMovement);
697 break;
699 case XK_Return:
700 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
701 (void*)WMReturnTextMovement);
702 break;
704 case WM_EMACSKEY_LEFT:
705 if (!control_pressed) {
706 goto normal_key;
708 case XK_KP_Left:
709 case XK_Left:
710 if (tPtr->cursorPosition > 0) {
711 paintCursor(tPtr);
712 if (event->xkey.state & ControlMask) {
713 int i;
714 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
715 if (tPtr->text[i] == ' ' || i == 0) {
716 tPtr->cursorPosition = i;
717 break;
719 } else {
720 tPtr->cursorPosition--;
722 if (tPtr->cursorPosition < tPtr->viewPosition) {
723 tPtr->viewPosition = tPtr->cursorPosition;
724 refresh = 1;
725 } else {
726 paintCursor(tPtr);
729 break;
731 case WM_EMACSKEY_RIGHT:
732 if (!control_pressed) {
733 goto normal_key;
735 case XK_KP_Right:
736 case XK_Right:
737 if (tPtr->cursorPosition < tPtr->textLen) {
738 paintCursor(tPtr);
739 if (event->xkey.state & ControlMask) {
740 int i;
741 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
742 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
743 tPtr->cursorPosition = i;
744 break;
746 } else {
747 tPtr->cursorPosition++;
749 while (WMWidthOfString(scr->normalFont,
750 &(tPtr->text[tPtr->viewPosition]),
751 tPtr->cursorPosition-tPtr->viewPosition)
752 > tPtr->usableWidth) {
753 tPtr->viewPosition++;
754 refresh = 1;
756 if (!refresh)
757 paintCursor(tPtr);
759 break;
761 case WM_EMACSKEY_HOME:
762 if (!control_pressed) {
763 goto normal_key;
765 case XK_KP_Home:
766 case XK_Home:
767 if (tPtr->cursorPosition > 0) {
768 paintCursor(tPtr);
769 tPtr->cursorPosition = 0;
770 if (tPtr->viewPosition > 0) {
771 tPtr->viewPosition = 0;
772 refresh = 1;
773 } else {
774 paintCursor(tPtr);
777 break;
779 case WM_EMACSKEY_END:
780 if (!control_pressed) {
781 goto normal_key;
783 case XK_KP_End:
784 case XK_End:
785 if (tPtr->cursorPosition < tPtr->textLen) {
786 paintCursor(tPtr);
787 tPtr->cursorPosition = tPtr->textLen;
788 tPtr->viewPosition = 0;
789 while (WMWidthOfString(scr->normalFont,
790 &(tPtr->text[tPtr->viewPosition]),
791 tPtr->textLen-tPtr->viewPosition)
792 > tPtr->usableWidth) {
793 tPtr->viewPosition++;
794 refresh = 1;
796 if (!refresh)
797 paintCursor(tPtr);
799 break;
801 case WM_EMACSKEY_BS:
802 if (!control_pressed) {
803 goto normal_key;
805 case XK_BackSpace:
806 if (tPtr->cursorPosition > 0) {
807 WMRange range;
808 changed = 1;
809 if (tPtr->prevselection.count) {
810 range.position = tPtr->prevselection.count < 0
811 ? tPtr->prevselection.position + tPtr->prevselection.count
812 : tPtr->prevselection.position;
814 range.count = abs(tPtr->prevselection.count);
815 } else {
816 range.position = tPtr->cursorPosition - 1;
817 range.count = 1;
819 WMDeleteTextFieldRange(tPtr, range);
821 break;
823 case WM_EMACSKEY_DEL:
824 if (!control_pressed) {
825 goto normal_key;
827 case XK_KP_Delete:
828 case XK_Delete:
829 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
830 WMRange range;
831 changed = 1;
832 if (tPtr->prevselection.count) {
833 range.position = tPtr->prevselection.count < 0
834 ? tPtr->prevselection.position + tPtr->prevselection.count
835 : tPtr->prevselection.position;
837 range.count = abs(tPtr->prevselection.count);
838 } else {
839 range.position = tPtr->cursorPosition;
840 range.count = 1;
842 WMDeleteTextFieldRange(tPtr, range);
844 break;
846 normal_key:
847 default:
848 if (count > 0 && !iscntrl(buffer[0])) {
849 WMRange range;
850 changed = 1;
851 if (tPtr->prevselection.count) {
852 range.position = tPtr->prevselection.count < 0
853 ? tPtr->prevselection.position + tPtr->prevselection.count
854 : tPtr->prevselection.position;
856 range.count = abs(tPtr->prevselection.count);
857 } else {
858 range.position = tPtr->cursorPosition;
859 range.count = 1;
861 if (tPtr->prevselection.count)
862 WMDeleteTextFieldRange(tPtr, range);
863 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
864 } else {
865 return;
867 break;
869 if (event->xkey.state & ShiftMask) {
870 if (tPtr->selection.count == 0)
871 tPtr->selection.position = tPtr->cursorPosition;
872 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
873 refresh = 1;
875 tPtr->prevselection.count = 0;
876 if (refresh) {
877 paintTextField(tPtr);
880 if (changed) {
881 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
886 static int
887 pointToCursorPosition(TextField *tPtr, int x)
889 WMFont *font = tPtr->view->screen->normalFont;
890 int a, b, mid;
891 int tw;
893 if (tPtr->flags.bordered)
894 x -= 2;
896 a = tPtr->viewPosition;
897 b = tPtr->viewPosition + tPtr->textLen;
898 if (WMWidthOfString(font, &(tPtr->text[tPtr->viewPosition]),
899 tPtr->textLen-tPtr->viewPosition) < x)
900 return tPtr->textLen;
902 while (a < b && b-a>1) {
903 mid = (a+b)/2;
904 tw = WMWidthOfString(font, &(tPtr->text[tPtr->viewPosition]),
905 mid - tPtr->viewPosition);
906 if (tw > x)
907 b = mid;
908 else if (tw < x)
909 a = mid;
910 else
911 return mid;
913 return (a+b)/2;
917 static void
918 handleTextFieldActionEvents(XEvent *event, void *data)
920 TextField *tPtr = (TextField*)data;
922 CHECK_CLASS(data, WC_TextField);
924 switch (event->type) {
925 case KeyPress:
926 if (tPtr->flags.enabled)
927 handleTextFieldKeyPress(tPtr, event);
928 break;
930 case MotionNotify:
931 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
933 if (!tPtr->selection.count) {
934 tPtr->selection.position = tPtr->cursorPosition;
937 tPtr->cursorPosition = pointToCursorPosition(tPtr,
938 event->xmotion.x);
940 tPtr->selection.count = tPtr->cursorPosition
941 - tPtr->selection.position;
943 paintTextField(tPtr);
945 break;
947 case ButtonPress:
948 if (tPtr->flags.enabled && !tPtr->flags.focused) {
949 WMSetFocusToWidget(tPtr);
950 tPtr->cursorPosition = pointToCursorPosition(tPtr,
951 event->xbutton.x);
952 paintTextField(tPtr);
953 } else if (tPtr->flags.focused) {
954 tPtr->cursorPosition = pointToCursorPosition(tPtr,
955 event->xbutton.x);
956 tPtr->selection.count = 0;
957 paintTextField(tPtr);
959 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
960 char *text;
962 text = W_GetTextSelection(tPtr->view->screen,
963 tPtr->view->screen->clipboardAtom);
964 if (!text) {
965 text = W_GetTextSelection(tPtr->view->screen, XA_CUT_BUFFER0);
967 if (text) {
968 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
969 XFree(text);
970 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
971 NULL);
974 break;
976 case ButtonRelease:
978 break;
983 static void
984 destroyTextField(TextField *tPtr)
986 #if 0
987 if (tPtr->timerID)
988 WMDeleteTimerHandler(tPtr->timerID);
989 #endif
991 if (tPtr->text)
992 free(tPtr->text);
994 free(tPtr);