removed notification posting for didChange when text is changed programmatically
[wmaker-crm.git] / WINGs / wtextfield.c
blob4d461227c852bb3c189d4e42c33c28429be2e644
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
17 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
18 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
19 char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
22 typedef struct W_TextField {
23 W_Class widgetClass;
24 W_View *view;
26 #if 0
27 struct W_TextField *nextField; /* next textfield in the chain */
28 struct W_TextField *prevField;
29 #endif
31 char *text;
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 */
39 short usableWidth;
40 short offsetWidth; /* offset of text from border */
42 WMRange selection;
43 WMRange prevselection;
45 WMFont *font;
47 WMTextFieldDelegate *delegate;
49 #if 0
50 WMHandlerID timerID; /* for cursor blinking */
51 #endif
52 struct {
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;
69 /**/
70 unsigned int notIllegalMovement:1;
71 } flags;
72 } TextField;
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 = {
112 NULL,
113 resizeTextField,
114 NULL
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))
125 static void
126 memmv(char *dest, char *src, int size)
128 int i;
130 if (dest > src) {
131 for (i=size-1; i>=0; i--) {
132 dest[i] = src[i];
134 } else if (dest < src) {
135 for (i=0; i<size; i++) {
136 dest[i] = src[i];
142 static int
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;
154 static int
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;
167 static void
168 decrToFit(TextField *tPtr)
170 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
171 && tPtr->viewPosition>0)
172 tPtr->viewPosition--;
175 #undef TEXT_WIDTH
176 #undef TEXT_WIDTH2
179 WMTextField*
180 WMCreateTextField(WMWidget *parent)
182 TextField *tPtr;
184 tPtr = wmalloc(sizeof(TextField));
185 memset(tPtr, 0, sizeof(TextField));
187 tPtr->widgetClass = WC_TextField;
189 tPtr->view = W_CreateView(W_VIEW(parent));
190 if (!tPtr->view) {
191 free(tPtr);
192 return NULL;
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);
202 tPtr->text[0] = 0;
203 tPtr->textLen = 0;
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;
218 tPtr->offsetWidth =
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;
227 return tPtr;
231 void
232 WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
234 tPtr->delegate = delegate;
238 void
239 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
241 int len;
243 CHECK_CLASS(tPtr, WC_TextField);
245 if (!text)
246 return;
248 len = strlen(text);
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);
260 incrToFit(tPtr);
262 tPtr->textLen += len;
263 tPtr->cursorPosition += len;
264 } else {
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;
274 incrToFit2(tPtr);
275 } else {
276 incrToFit(tPtr);
280 paintTextField(tPtr);
284 static void
285 deleteTextFieldRange(WMTextField *tPtr, WMRange range)
287 CHECK_CLASS(tPtr, WC_TextField);
289 if (range.position >= tPtr->textLen)
290 return;
292 if (range.count < 1) {
293 if (range.position < 0)
294 range.position = 0;
295 tPtr->text[range.position] = 0;
296 tPtr->textLen = range.position;
298 tPtr->cursorPosition = 0;
299 tPtr->viewPosition = 0;
300 } else {
301 if (range.position + range.count > tPtr->textLen)
302 range.count = tPtr->textLen - range.position;
303 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
304 tPtr->textLen - (range.position+range.count) + 1);
305 tPtr->textLen -= range.count;
307 if (tPtr->cursorPosition > range.position)
308 tPtr->cursorPosition -= range.count;
310 decrToFit(tPtr);
313 paintTextField(tPtr);
317 void
318 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
320 deleteTextFieldRange(tPtr, range);
325 char*
326 WMGetTextFieldText(WMTextField *tPtr)
328 CHECK_CLASS(tPtr, WC_TextField);
330 return wstrdup(tPtr->text);
334 void
335 WMSetTextFieldText(WMTextField *tPtr, char *text)
337 if ((text && strcmp(tPtr->text, text) == 0) ||
338 (!text && tPtr->textLen == 0))
339 return;
341 if (text==NULL) {
342 tPtr->text[0] = 0;
343 tPtr->textLen = 0;
344 } else {
345 tPtr->textLen = strlen(text);
347 if (tPtr->textLen >= tPtr->bufferSize) {
348 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
349 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
351 strcpy(tPtr->text, text);
354 if (tPtr->textLen < tPtr->cursorPosition)
355 tPtr->cursorPosition = tPtr->textLen;
357 tPtr->cursorPosition = tPtr->textLen;
358 tPtr->viewPosition = 0;
359 tPtr->selection.count = 0;
361 if (tPtr->view->flags.realized)
362 paintTextField(tPtr);
366 void
367 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
369 /* TODO: update font change after field is mapped */
370 WMReleaseFont(tPtr->font);
371 tPtr->font = WMRetainFont(font);
375 void
376 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
378 tPtr->flags.alignment = alignment;
379 if (alignment!=WALeft) {
380 wwarning("only left alignment is supported in textfields");
381 return;
384 if (tPtr->view->flags.realized) {
385 paintTextField(tPtr);
390 void
391 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
393 tPtr->flags.bordered = bordered;
395 if (tPtr->view->flags.realized) {
396 paintTextField(tPtr);
401 void
402 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
404 tPtr->flags.beveled = flag;
406 if (tPtr->view->flags.realized) {
407 paintTextField(tPtr);
413 void
414 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
416 tPtr->flags.secure = flag;
418 if (tPtr->view->flags.realized) {
419 paintTextField(tPtr);
424 Bool
425 WMGetTextFieldEditable(WMTextField *tPtr)
427 return tPtr->flags.enabled;
431 void
432 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
434 tPtr->flags.enabled = flag;
436 if (tPtr->view->flags.realized) {
437 paintTextField(tPtr);
442 void
443 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
445 if (tPtr->flags.enabled) {
446 if (range.position < 0) {
447 range.count += range.position;
448 range.count = (range.count < 0) ? 0 : range.count;
449 range.position = 0;
450 } else if (range.position > tPtr->textLen) {
451 range.position = tPtr->textLen;
452 range.count = 0;
455 if (range.position + range.count > tPtr->textLen)
456 range.count = tPtr->textLen - range.position;
458 tPtr->prevselection = tPtr->selection; /* check if this is needed */
460 tPtr->selection = range;
462 if (tPtr->view->flags.realized) {
463 paintTextField(tPtr);
469 void
470 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
472 if (tPtr->flags.enabled) {
473 if (position > tPtr->textLen)
474 position = tPtr->textLen;
476 tPtr->cursorPosition = position;
477 if (tPtr->view->flags.realized) {
478 paintTextField(tPtr);
484 void
485 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
487 CHECK_CLASS(tPtr, WC_TextField);
488 if (next == NULL) {
489 if (tPtr->view->nextFocusChain)
490 tPtr->view->nextFocusChain->prevFocusChain = NULL;
491 tPtr->view->nextFocusChain = NULL;
492 return;
495 CHECK_CLASS(next, WC_TextField);
497 if (tPtr->view->nextFocusChain)
498 tPtr->view->nextFocusChain->prevFocusChain = NULL;
499 if (next->view->prevFocusChain)
500 next->view->prevFocusChain->nextFocusChain = NULL;
502 tPtr->view->nextFocusChain = next->view;
503 next->view->prevFocusChain = tPtr->view;
507 void
508 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
510 CHECK_CLASS(tPtr, WC_TextField);
511 if (prev == NULL) {
512 if (tPtr->view->prevFocusChain)
513 tPtr->view->prevFocusChain->nextFocusChain = NULL;
514 tPtr->view->prevFocusChain = NULL;
515 return;
518 CHECK_CLASS(prev, WC_TextField);
520 if (tPtr->view->prevFocusChain)
521 tPtr->view->prevFocusChain->nextFocusChain = NULL;
522 if (prev->view->nextFocusChain)
523 prev->view->nextFocusChain->prevFocusChain = NULL;
525 tPtr->view->prevFocusChain = prev->view;
526 prev->view->nextFocusChain = tPtr->view;
530 static void
531 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
533 W_ResizeView(tPtr->view, width, height);
535 tPtr->offsetWidth =
536 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
538 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
542 static char*
543 makeHiddenString(int length)
545 char *data = wmalloc(length+1);
547 memset(data, '*', length);
548 data[length] = '\0';
549 return data;
553 static void
554 paintCursor(TextField *tPtr)
556 int cx;
557 WMScreen *screen = tPtr->view->screen;
558 int textWidth;
559 char *text;
561 if (tPtr->flags.secure)
562 text = makeHiddenString(strlen(tPtr->text));
563 else
564 text = tPtr->text;
566 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
567 tPtr->cursorPosition-tPtr->viewPosition);
569 switch (tPtr->flags.alignment) {
570 case WARight:
571 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
572 if (textWidth < tPtr->usableWidth)
573 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
574 else
575 cx += tPtr->offsetWidth + 1;
576 break;
577 case WALeft:
578 cx += tPtr->offsetWidth + 1;
579 break;
580 case WAJustified:
581 /* not supported */
582 case WACenter:
583 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
584 if (textWidth < tPtr->usableWidth)
585 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
586 else
587 cx += tPtr->offsetWidth;
588 break;
591 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
592 cx, tPtr->offsetWidth, 1,
593 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
594 printf("%d %d\n",cx,tPtr->cursorPosition);
596 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
597 cx, tPtr->offsetWidth, cx,
598 tPtr->view->size.height - tPtr->offsetWidth - 1);
600 if (tPtr->flags.secure)
601 free(text);
606 static void
607 drawRelief(WMView *view, Bool beveled)
609 WMScreen *scr = view->screen;
610 Display *dpy = scr->display;
611 GC wgc;
612 GC lgc;
613 GC dgc;
614 int width = view->size.width;
615 int height = view->size.height;
617 dgc = WMColorGC(scr->darkGray);
619 if (!beveled) {
620 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
622 return;
624 wgc = WMColorGC(scr->white);
625 lgc = WMColorGC(scr->gray);
627 /* top left */
628 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
629 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
631 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
632 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
634 /* bottom right */
635 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
636 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
638 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
639 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
643 static void
644 paintTextField(TextField *tPtr)
646 W_Screen *screen = tPtr->view->screen;
647 W_View *view = tPtr->view;
648 W_View viewbuffer;
649 int tx, ty, tw, th;
650 int rx;
651 int bd;
652 int totalWidth;
653 char *text;
654 Pixmap drawbuffer;
657 if (!view->flags.realized || !view->flags.mapped)
658 return;
660 if (!tPtr->flags.bordered) {
661 bd = 0;
662 } else {
663 bd = 2;
666 if (tPtr->flags.secure) {
667 text = makeHiddenString(strlen(tPtr->text));
668 } else {
669 text = tPtr->text;
672 totalWidth = tPtr->view->size.width - 2*bd;
674 drawbuffer = XCreatePixmap(screen->display, view->window,
675 view->size.width, view->size.height, screen->depth);
676 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
677 0,0, view->size.width,view->size.height);
678 /* this is quite dirty */
679 viewbuffer.screen = view->screen;
680 viewbuffer.size = view->size;
681 viewbuffer.window = drawbuffer;
684 if (tPtr->textLen > 0) {
685 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
686 tPtr->textLen - tPtr->viewPosition);
688 th = WMFontHeight(tPtr->font);
690 ty = tPtr->offsetWidth;
691 switch (tPtr->flags.alignment) {
692 case WALeft:
693 tx = tPtr->offsetWidth + 1;
694 if (tw < tPtr->usableWidth)
695 XFillRectangle(screen->display, drawbuffer,
696 WMColorGC(screen->white),
697 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
698 break;
700 case WACenter:
701 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
702 if (tw < tPtr->usableWidth)
703 XClearArea(screen->display, view->window, bd, bd,
704 totalWidth, view->size.height-2*bd, False);
705 break;
707 default:
708 case WARight:
709 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
710 if (tw < tPtr->usableWidth)
711 XClearArea(screen->display, view->window, bd, bd,
712 totalWidth-tw, view->size.height-2*bd, False);
713 break;
716 if (!tPtr->flags.enabled)
717 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
719 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
720 tPtr->font, tx, ty,
721 &(text[tPtr->viewPosition]),
722 tPtr->textLen - tPtr->viewPosition);
724 if (tPtr->selection.count) {
725 int count,count2;
727 count = tPtr->selection.count < 0
728 ? tPtr->selection.position + tPtr->selection.count
729 : tPtr->selection.position;
730 count2 = abs(tPtr->selection.count);
731 if (count < tPtr->viewPosition) {
732 count2 = abs(count2 - abs(tPtr->viewPosition - count));
733 count = tPtr->viewPosition;
737 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
738 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
740 XSetBackground(screen->display, screen->textFieldGC,
741 screen->gray->color.pixel);
743 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
744 tPtr->font, rx, ty, &(text[count]),
745 count2);
747 XSetBackground(screen->display, screen->textFieldGC,
748 screen->white->color.pixel);
751 if (!tPtr->flags.enabled)
752 WMSetColorInGC(screen->black, screen->textFieldGC);
753 } else {
754 XFillRectangle(screen->display, drawbuffer,
755 WMColorGC(screen->white),
756 bd,bd, totalWidth,view->size.height-2*bd);
759 /* draw relief */
760 if (tPtr->flags.bordered) {
761 drawRelief(&viewbuffer, tPtr->flags.beveled);
764 if (tPtr->flags.secure)
765 free(text);
766 XCopyArea(screen->display, drawbuffer, view->window,
767 screen->copyGC, 0,0, view->size.width,
768 view->size.height,0,0);
769 XFreePixmap(screen->display, drawbuffer);
771 /* draw cursor */
772 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
773 paintCursor(tPtr);
778 #if 0
779 static void
780 blinkCursor(void *data)
782 TextField *tPtr = (TextField*)data;
784 if (tPtr->flags.cursorOn) {
785 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
786 data);
787 } else {
788 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
789 data);
791 paintCursor(tPtr);
792 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
794 #endif
797 static void
798 handleEvents(XEvent *event, void *data)
800 TextField *tPtr = (TextField*)data;
802 CHECK_CLASS(data, WC_TextField);
805 switch (event->type) {
806 case FocusIn:
807 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
808 return;
809 tPtr->flags.focused = 1;
810 #if 0
811 if (!tPtr->timerID) {
812 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
813 blinkCursor, tPtr);
815 #endif
816 paintTextField(tPtr);
818 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
820 tPtr->flags.notIllegalMovement = 0;
821 break;
823 case FocusOut:
824 tPtr->flags.focused = 0;
825 #if 0
826 if (tPtr->timerID)
827 WMDeleteTimerHandler(tPtr->timerID);
828 tPtr->timerID = NULL;
829 #endif
831 paintTextField(tPtr);
832 if (!tPtr->flags.notIllegalMovement) {
833 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
834 (void*)WMIllegalTextMovement);
836 break;
838 case Expose:
839 if (event->xexpose.count!=0)
840 break;
841 paintTextField(tPtr);
842 break;
844 case DestroyNotify:
845 destroyTextField(tPtr);
846 break;
851 static void
852 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
854 char buffer[64];
855 KeySym ksym;
856 int count, refresh = 0;
857 int control_pressed = 0;
859 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
860 control_pressed = 1;
863 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
864 buffer[count] = '\0';
866 if (!(event->xkey.state & ShiftMask)) {
867 if (tPtr->selection.count)
868 refresh = 1;
869 tPtr->prevselection = tPtr->selection;
870 tPtr->selection.position = tPtr->cursorPosition;
871 tPtr->selection.count = 0;
874 /* Be careful in any case in this switch statement, never to call
875 * to more than a function that can generate text change notifications.
876 * Only one text change notification should be sent in any case.
877 * Else hazardous things can happen.
878 * Maybe we need a better solution than the function wrapper to inform
879 * functions that change text in text fields, if they need to send a
880 * change notification or not. -Dan
882 switch (ksym) {
883 case XK_Tab:
884 case XK_ISO_Left_Tab:
885 if (event->xkey.state & ShiftMask) {
886 if (tPtr->view->prevFocusChain) {
887 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
888 tPtr->view->prevFocusChain);
889 tPtr->flags.notIllegalMovement = 1;
891 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
892 (void*)WMBacktabTextMovement);
893 } else {
894 if (tPtr->view->nextFocusChain) {
895 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
896 tPtr->view->nextFocusChain);
897 tPtr->flags.notIllegalMovement = 1;
899 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
900 (void*)WMTabTextMovement);
902 break;
904 case XK_Return:
905 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
906 (void*)WMReturnTextMovement);
907 break;
909 case WM_EMACSKEY_LEFT:
910 if (!control_pressed) {
911 goto normal_key;
913 case XK_KP_Left:
914 case XK_Left:
915 if (tPtr->cursorPosition > 0) {
916 paintCursor(tPtr);
917 if (event->xkey.state & ControlMask) {
918 int i;
919 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
920 if (tPtr->text[i] == ' ' || i == 0) {
921 tPtr->cursorPosition = i;
922 break;
924 } else {
925 tPtr->cursorPosition--;
927 if (tPtr->cursorPosition < tPtr->viewPosition) {
928 tPtr->viewPosition = tPtr->cursorPosition;
929 refresh = 1;
930 } else {
931 paintCursor(tPtr);
934 break;
936 case WM_EMACSKEY_RIGHT:
937 if (!control_pressed) {
938 goto normal_key;
940 case XK_KP_Right:
941 case XK_Right:
942 if (tPtr->cursorPosition < tPtr->textLen) {
943 paintCursor(tPtr);
944 if (event->xkey.state & ControlMask) {
945 int i;
946 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
947 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
948 tPtr->cursorPosition = i;
949 break;
951 } else {
952 tPtr->cursorPosition++;
954 while (WMWidthOfString(tPtr->font,
955 &(tPtr->text[tPtr->viewPosition]),
956 tPtr->cursorPosition-tPtr->viewPosition)
957 > tPtr->usableWidth) {
958 tPtr->viewPosition++;
959 refresh = 1;
961 if (!refresh)
962 paintCursor(tPtr);
964 break;
966 case WM_EMACSKEY_HOME:
967 if (!control_pressed) {
968 goto normal_key;
970 case XK_KP_Home:
971 case XK_Home:
972 if (tPtr->cursorPosition > 0) {
973 paintCursor(tPtr);
974 tPtr->cursorPosition = 0;
975 if (tPtr->viewPosition > 0) {
976 tPtr->viewPosition = 0;
977 refresh = 1;
978 } else {
979 paintCursor(tPtr);
982 break;
984 case WM_EMACSKEY_END:
985 if (!control_pressed) {
986 goto normal_key;
988 case XK_KP_End:
989 case XK_End:
990 if (tPtr->cursorPosition < tPtr->textLen) {
991 paintCursor(tPtr);
992 tPtr->cursorPosition = tPtr->textLen;
993 tPtr->viewPosition = 0;
994 while (WMWidthOfString(tPtr->font,
995 &(tPtr->text[tPtr->viewPosition]),
996 tPtr->textLen-tPtr->viewPosition)
997 > tPtr->usableWidth) {
998 tPtr->viewPosition++;
999 refresh = 1;
1001 if (!refresh)
1002 paintCursor(tPtr);
1004 break;
1006 case WM_EMACSKEY_BS:
1007 if (!control_pressed) {
1008 goto normal_key;
1010 case XK_BackSpace:
1011 if (tPtr->cursorPosition > 0) {
1012 WMRange range;
1014 if (tPtr->prevselection.count) {
1015 range.position = tPtr->prevselection.count < 0
1016 ? tPtr->prevselection.position + tPtr->prevselection.count
1017 : tPtr->prevselection.position;
1019 range.count = abs(tPtr->prevselection.count);
1020 } else {
1021 range.position = tPtr->cursorPosition - 1;
1022 range.count = 1;
1024 WMDeleteTextFieldRange(tPtr, range);
1025 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1027 break;
1029 case WM_EMACSKEY_DEL:
1030 if (!control_pressed) {
1031 goto normal_key;
1033 case XK_KP_Delete:
1034 case XK_Delete:
1035 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1036 WMRange range;
1038 if (tPtr->prevselection.count) {
1039 range.position = tPtr->prevselection.count < 0
1040 ? tPtr->prevselection.position + tPtr->prevselection.count
1041 : tPtr->prevselection.position;
1043 range.count = abs(tPtr->prevselection.count);
1044 } else {
1045 range.position = tPtr->cursorPosition;
1046 range.count = 1;
1048 WMDeleteTextFieldRange(tPtr, range);
1049 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1051 break;
1053 normal_key:
1054 default:
1055 if (count > 0 && !iscntrl(buffer[0])) {
1056 WMRange range;
1058 if (tPtr->prevselection.count) {
1059 range.position = tPtr->prevselection.count < 0
1060 ? tPtr->prevselection.position + tPtr->prevselection.count
1061 : tPtr->prevselection.position;
1063 range.count = abs(tPtr->prevselection.count);
1064 } else {
1065 range.position = tPtr->cursorPosition;
1066 range.count = 1;
1068 if (tPtr->prevselection.count)
1069 deleteTextFieldRange(tPtr, range);
1070 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1071 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1072 } else {
1073 return;
1075 break;
1077 if (event->xkey.state & ShiftMask) {
1078 if (tPtr->selection.count == 0)
1079 tPtr->selection.position = tPtr->cursorPosition;
1080 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1081 refresh = 1;
1083 tPtr->prevselection.count = 0;
1084 if (refresh) {
1085 paintTextField(tPtr);
1090 static int
1091 pointToCursorPosition(TextField *tPtr, int x)
1093 int a, b, mid;
1094 int tw;
1096 if (tPtr->flags.bordered)
1097 x -= 2;
1099 a = tPtr->viewPosition;
1100 b = tPtr->viewPosition + tPtr->textLen;
1101 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1102 tPtr->textLen - tPtr->viewPosition) < x)
1103 return tPtr->textLen;
1105 while (a < b && b-a>1) {
1106 mid = (a+b)/2;
1107 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1108 mid - tPtr->viewPosition);
1109 if (tw > x)
1110 b = mid;
1111 else if (tw < x)
1112 a = mid;
1113 else
1114 return mid;
1116 return (a+b)/2;
1120 static void
1121 handleTextFieldActionEvents(XEvent *event, void *data)
1123 TextField *tPtr = (TextField*)data;
1124 static int move;
1126 CHECK_CLASS(data, WC_TextField);
1128 switch (event->type) {
1129 case KeyPress:
1130 if (tPtr->flags.enabled && tPtr->flags.focused) {
1131 handleTextFieldKeyPress(tPtr, event);
1132 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1133 W_VIEW(tPtr)->window, False,
1134 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1135 GrabModeAsync, GrabModeAsync, None,
1136 W_VIEW(tPtr)->screen->invisibleCursor,
1137 CurrentTime);
1138 tPtr->flags.pointerGrabbed = 1;
1140 break;
1142 case MotionNotify:
1144 if (tPtr->flags.pointerGrabbed) {
1145 tPtr->flags.pointerGrabbed = 0;
1146 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1149 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1151 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1152 tPtr->usableWidth) {
1153 if (WMWidthOfString(tPtr->font,
1154 &(tPtr->text[tPtr->viewPosition]),
1155 tPtr->cursorPosition-tPtr->viewPosition)
1156 > tPtr->usableWidth) {
1157 tPtr->viewPosition++;
1159 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1160 paintCursor(tPtr);
1161 tPtr->viewPosition--;
1164 if (!tPtr->selection.count) {
1165 tPtr->selection.position = tPtr->cursorPosition;
1168 tPtr->cursorPosition =
1169 pointToCursorPosition(tPtr, event->xmotion.x);
1171 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1174 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1177 paintCursor(tPtr);
1178 paintTextField(tPtr);
1181 if (move) {
1182 int count;
1183 XSetSelectionOwner(tPtr->view->screen->display,
1184 XA_PRIMARY, None, CurrentTime);
1185 count = tPtr->selection.count < 0
1186 ? tPtr->selection.position + tPtr->selection.count
1187 : tPtr->selection.position;
1188 XStoreBuffer(tPtr->view->screen->display,
1189 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1191 break;
1193 case ButtonPress:
1194 if (tPtr->flags.pointerGrabbed) {
1195 tPtr->flags.pointerGrabbed = 0;
1196 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1197 break;
1200 move = 1;
1201 switch (tPtr->flags.alignment) {
1202 int textWidth;
1203 case WARight:
1204 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1205 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1206 WMSetFocusToWidget(tPtr);
1207 } else if (tPtr->flags.focused) {
1208 tPtr->selection.count = 0;
1210 if(textWidth < tPtr->usableWidth){
1211 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1212 event->xbutton.x - tPtr->usableWidth
1213 + textWidth);
1215 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1216 event->xbutton.x);
1218 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1219 event->xbutton.x);
1220 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1223 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1224 event->xbutton.x);
1226 paintTextField(tPtr);
1227 break;
1229 case WALeft:
1230 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1231 WMSetFocusToWidget(tPtr);
1232 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1233 event->xbutton.x);
1234 paintTextField(tPtr);
1235 } else if (tPtr->flags.focused) {
1236 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1237 event->xbutton.x);
1238 tPtr->selection.count = 0;
1239 paintTextField(tPtr);
1241 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1242 char *text;
1244 text = W_GetTextSelection(tPtr->view->screen,
1245 tPtr->view->screen->clipboardAtom);
1246 if (!text) {
1247 text = W_GetTextSelection(tPtr->view->screen,
1248 XA_CUT_BUFFER0);
1250 if (text) {
1251 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1252 XFree(text);
1253 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1256 break;
1258 break;
1260 case ButtonRelease:
1261 if (tPtr->flags.pointerGrabbed) {
1262 tPtr->flags.pointerGrabbed = 0;
1263 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1266 move = 0;
1267 break;
1272 static void
1273 destroyTextField(TextField *tPtr)
1275 #if 0
1276 if (tPtr->timerID)
1277 WMDeleteTimerHandler(tPtr->timerID);
1278 #endif
1280 WMReleaseFont(tPtr->font);
1282 if (tPtr->text)
1283 free(tPtr->text);
1285 free(tPtr);