fixed font bug in WINGs
[wmaker-crm.git] / WINGs / wtextfield.c
blobd68021b9c3215295be365588c3f7b629aec718e9
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 didResizeTextField();
111 struct W_ViewDelegate _TextFieldViewDelegate = {
112 NULL,
113 NULL,
114 didResizeTextField,
115 NULL,
116 NULL
120 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
121 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
123 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
124 &((tPtr)->text[(start)]), (end) - (start) + 1))
127 static void
128 memmv(char *dest, char *src, int size)
130 int i;
132 if (dest > src) {
133 for (i=size-1; i>=0; i--) {
134 dest[i] = src[i];
136 } else if (dest < src) {
137 for (i=0; i<size; i++) {
138 dest[i] = src[i];
144 static int
145 incrToFit(TextField *tPtr)
147 int vp = tPtr->viewPosition;
149 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
150 tPtr->viewPosition++;
152 return vp!=tPtr->viewPosition;
156 static int
157 incrToFit2(TextField *tPtr)
159 int vp = tPtr->viewPosition;
160 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
161 >= tPtr->usableWidth)
162 tPtr->viewPosition++;
165 return vp!=tPtr->viewPosition;
169 static void
170 decrToFit(TextField *tPtr)
172 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
173 && tPtr->viewPosition>0)
174 tPtr->viewPosition--;
177 #undef TEXT_WIDTH
178 #undef TEXT_WIDTH2
180 static Bool
181 requestHandler(WMWidget *w, Atom selection, Atom target, Atom *type,
182 void **value, unsigned *length, int *format)
184 TextField *tPtr = w;
185 int count;
186 Display *dpy = tPtr->view->screen->display;
187 Atom _TARGETS;
188 char *text;
189 text = XGetAtomName(tPtr->view->screen->display,target);
190 XFree(text);
191 text = XGetAtomName(tPtr->view->screen->display,selection);
192 XFree(text);
194 *format = 32;
195 *length = 0;
196 *value = NULL;
197 count = tPtr->selection.count < 0
198 ? tPtr->selection.position + tPtr->selection.count
199 : tPtr->selection.position;
201 if (target == XA_STRING ||
202 target == XInternAtom(dpy, "TEXT", False) ||
203 target == XInternAtom(dpy, "COMPOUND_TEXT", False)) {
204 *value = wstrdup(&(tPtr->text[count]));
205 *length = abs(tPtr->selection.count);
206 *format = 8;
207 *type = target;
208 return True;
211 _TARGETS = XInternAtom(dpy, "TARGETS", False);
212 if (target == _TARGETS) {
213 int *ptr;
215 *length = 4;
216 ptr = *value = (char *) wmalloc(4 * sizeof(Atom));
217 ptr[0] = _TARGETS;
218 ptr[1] = XA_STRING;
219 ptr[2] = XInternAtom(dpy, "TEXT", False);
220 ptr[3] = XInternAtom(dpy, "COMPOUND_TEXT", False);
221 *type = target;
222 return True;
225 *target = XA_PRIMARY;
227 return False;
232 static void
233 lostHandler(WMWidget *w, Atom selection)
235 TextField *tPtr = (WMTextField*)w;
237 tPtr->selection.count = 0;
238 paintTextField(tPtr);
241 static void
242 _notification(void *observerData, WMNotification *notification)
244 WMTextField *to = (WMTextField*)observerData;
245 WMTextField *tw = (WMTextField*)WMGetNotificationClientData(notification);
246 if (to != tw) lostHandler(to, 0);
249 WMTextField*
250 WMCreateTextField(WMWidget *parent)
252 TextField *tPtr;
254 tPtr = wmalloc(sizeof(TextField));
255 memset(tPtr, 0, sizeof(TextField));
257 tPtr->widgetClass = WC_TextField;
259 tPtr->view = W_CreateView(W_VIEW(parent));
260 if (!tPtr->view) {
261 free(tPtr);
262 return NULL;
264 tPtr->view->self = tPtr;
266 tPtr->view->delegate = &_TextFieldViewDelegate;
268 tPtr->view->attribFlags |= CWCursor;
269 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
271 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
273 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
274 tPtr->text[0] = 0;
275 tPtr->textLen = 0;
276 tPtr->bufferSize = MIN_TEXT_BUFFER;
278 tPtr->flags.enabled = 1;
280 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
281 |FocusChangeMask, handleEvents, tPtr);
283 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
285 tPtr->flags.bordered = DEFAULT_BORDERED;
286 tPtr->flags.beveled = True;
287 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
288 tPtr->offsetWidth =
289 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
291 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
293 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
294 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
295 handleTextFieldActionEvents, tPtr);
297 WMCreateSelectionHandler(tPtr, XA_PRIMARY, CurrentTime, requestHandler,
298 lostHandler, NULL);
299 WMAddNotificationObserver(_notification, tPtr, "_lostOwnership", tPtr);
302 tPtr->flags.cursorOn = 1;
304 return tPtr;
308 void
309 WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
311 tPtr->delegate = delegate;
315 void
316 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
318 int len;
320 CHECK_CLASS(tPtr, WC_TextField);
322 if (!text)
323 return;
325 len = strlen(text);
327 /* check if buffer will hold the text */
328 if (len + tPtr->textLen >= tPtr->bufferSize) {
329 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
330 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
333 if (position < 0 || position >= tPtr->textLen) {
334 /* append the text at the end */
335 strcat(tPtr->text, text);
337 incrToFit(tPtr);
339 tPtr->textLen += len;
340 tPtr->cursorPosition += len;
341 } else {
342 /* insert text at position */
343 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
344 tPtr->textLen-position+1);
346 memcpy(&(tPtr->text[position]), text, len);
348 tPtr->textLen += len;
349 if (position >= tPtr->cursorPosition) {
350 tPtr->cursorPosition += len;
351 incrToFit2(tPtr);
352 } else {
353 incrToFit(tPtr);
357 paintTextField(tPtr);
361 void
362 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
364 CHECK_CLASS(tPtr, WC_TextField);
366 if (range.position >= tPtr->textLen)
367 return;
369 if (range.count < 1) {
370 if (range.position < 0)
371 range.position = 0;
372 tPtr->text[range.position] = 0;
373 tPtr->textLen = range.position;
375 tPtr->cursorPosition = 0;
376 tPtr->viewPosition = 0;
377 } else {
378 if (range.position + range.count > tPtr->textLen)
379 range.count = tPtr->textLen - range.position;
380 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
381 tPtr->textLen - (range.position+range.count) + 1);
382 tPtr->textLen -= range.count;
384 if (tPtr->cursorPosition > range.position)
385 tPtr->cursorPosition -= range.count;
387 decrToFit(tPtr);
390 paintTextField(tPtr);
395 char*
396 WMGetTextFieldText(WMTextField *tPtr)
398 CHECK_CLASS(tPtr, WC_TextField);
400 return wstrdup(tPtr->text);
404 void
405 WMSetTextFieldText(WMTextField *tPtr, char *text)
407 if ((text && strcmp(tPtr->text, text) == 0) ||
408 (!text && tPtr->textLen == 0))
409 return;
411 if (text==NULL) {
412 tPtr->text[0] = 0;
413 tPtr->textLen = 0;
414 } else {
415 tPtr->textLen = strlen(text);
417 if (tPtr->textLen >= tPtr->bufferSize) {
418 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
419 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
421 strcpy(tPtr->text, text);
424 if (tPtr->textLen < tPtr->cursorPosition)
425 tPtr->cursorPosition = tPtr->textLen;
427 tPtr->cursorPosition = tPtr->textLen;
428 tPtr->viewPosition = 0;
429 tPtr->selection.count = 0;
431 if (tPtr->view->flags.realized)
432 paintTextField(tPtr);
436 void
437 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
439 tPtr->flags.alignment = alignment;
440 if (alignment!=WALeft) {
441 wwarning("only left alignment is supported in textfields");
442 return;
445 if (tPtr->view->flags.realized) {
446 paintTextField(tPtr);
451 void
452 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
454 tPtr->flags.bordered = bordered;
456 if (tPtr->view->flags.realized) {
457 paintTextField(tPtr);
462 void
463 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
465 tPtr->flags.beveled = flag;
467 if (tPtr->view->flags.realized) {
468 paintTextField(tPtr);
474 void
475 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
477 tPtr->flags.secure = flag;
479 if (tPtr->view->flags.realized) {
480 paintTextField(tPtr);
485 Bool
486 WMGetTextFieldEditable(WMTextField *tPtr)
488 return tPtr->flags.enabled;
492 void
493 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
495 tPtr->flags.enabled = flag;
497 if (tPtr->view->flags.realized) {
498 paintTextField(tPtr);
503 void
504 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
506 if (tPtr->flags.enabled) {
507 if (range.position < 0) {
508 range.count += range.position;
509 range.count = (range.count < 0) ? 0 : range.count;
510 range.position = 0;
511 } else if (range.position > tPtr->textLen) {
512 range.position = tPtr->textLen;
513 range.count = 0;
516 if (range.position + range.count > tPtr->textLen)
517 range.count = tPtr->textLen - range.position;
519 tPtr->prevselection = tPtr->selection; /* check if this is needed */
521 tPtr->selection = range;
523 if (tPtr->view->flags.realized) {
524 paintTextField(tPtr);
530 void
531 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
533 if (tPtr->flags.enabled) {
534 if (position > tPtr->textLen)
535 position = tPtr->textLen;
537 tPtr->cursorPosition = position;
538 if (tPtr->view->flags.realized) {
539 paintTextField(tPtr);
545 void
546 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
548 CHECK_CLASS(tPtr, WC_TextField);
549 if (next == NULL) {
550 if (tPtr->view->nextFocusChain)
551 tPtr->view->nextFocusChain->prevFocusChain = NULL;
552 tPtr->view->nextFocusChain = NULL;
553 return;
556 CHECK_CLASS(next, WC_TextField);
558 if (tPtr->view->nextFocusChain)
559 tPtr->view->nextFocusChain->prevFocusChain = NULL;
560 if (next->view->prevFocusChain)
561 next->view->prevFocusChain->nextFocusChain = NULL;
563 tPtr->view->nextFocusChain = next->view;
564 next->view->prevFocusChain = tPtr->view;
568 void
569 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
571 CHECK_CLASS(tPtr, WC_TextField);
572 if (prev == NULL) {
573 if (tPtr->view->prevFocusChain)
574 tPtr->view->prevFocusChain->nextFocusChain = NULL;
575 tPtr->view->prevFocusChain = NULL;
576 return;
579 CHECK_CLASS(prev, WC_TextField);
581 if (tPtr->view->prevFocusChain)
582 tPtr->view->prevFocusChain->nextFocusChain = NULL;
583 if (prev->view->nextFocusChain)
584 prev->view->nextFocusChain->prevFocusChain = NULL;
586 tPtr->view->prevFocusChain = prev->view;
587 prev->view->nextFocusChain = tPtr->view;
591 void
592 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
594 if (tPtr->font)
595 WMReleaseFont(tPtr->font);
596 tPtr->font = WMRetainFont(font);
598 tPtr->offsetWidth =
599 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
601 if (tPtr->view->flags.realized) {
602 paintTextField(tPtr);
607 WMFont*
608 WMGetTextFieldFont(WMTextField *tPtr)
610 return tPtr->font;
615 static void
616 didResizeTextField(W_ViewDelegate *self, WMView *view)
618 WMTextField *tPtr = (WMTextField*)view->self;
620 tPtr->offsetWidth =
621 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
623 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
627 static char*
628 makeHiddenString(int length)
630 char *data = wmalloc(length+1);
632 memset(data, '*', length);
633 data[length] = '\0';
635 return data;
639 static void
640 paintCursor(TextField *tPtr)
642 int cx;
643 WMScreen *screen = tPtr->view->screen;
644 int textWidth;
645 char *text;
647 if (tPtr->flags.secure)
648 text = makeHiddenString(strlen(tPtr->text));
649 else
650 text = tPtr->text;
652 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
653 tPtr->cursorPosition-tPtr->viewPosition);
655 switch (tPtr->flags.alignment) {
656 case WARight:
657 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
658 if (textWidth < tPtr->usableWidth)
659 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
660 else
661 cx += tPtr->offsetWidth + 1;
662 break;
663 case WALeft:
664 cx += tPtr->offsetWidth + 1;
665 break;
666 case WAJustified:
667 /* not supported */
668 case WACenter:
669 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
670 if (textWidth < tPtr->usableWidth)
671 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
672 else
673 cx += tPtr->offsetWidth;
674 break;
677 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
678 cx, tPtr->offsetWidth, 1,
679 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
680 printf("%d %d\n",cx,tPtr->cursorPosition);
682 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
683 cx, tPtr->offsetWidth, cx,
684 tPtr->view->size.height - tPtr->offsetWidth - 1);
686 if (tPtr->flags.secure)
687 free(text);
692 static void
693 drawRelief(WMView *view, Bool beveled)
695 WMScreen *scr = view->screen;
696 Display *dpy = scr->display;
697 GC wgc;
698 GC lgc;
699 GC dgc;
700 int width = view->size.width;
701 int height = view->size.height;
703 dgc = WMColorGC(scr->darkGray);
705 if (!beveled) {
706 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
708 return;
710 wgc = WMColorGC(scr->white);
711 lgc = WMColorGC(scr->gray);
713 /* top left */
714 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
715 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
717 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
718 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
720 /* bottom right */
721 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
722 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
724 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
725 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
729 static void
730 paintTextField(TextField *tPtr)
732 W_Screen *screen = tPtr->view->screen;
733 W_View *view = tPtr->view;
734 W_View viewbuffer;
735 int tx, ty, tw, th;
736 int rx;
737 int bd;
738 int totalWidth;
739 char *text;
740 Pixmap drawbuffer;
743 if (!view->flags.realized || !view->flags.mapped)
744 return;
746 if (!tPtr->flags.bordered) {
747 bd = 0;
748 } else {
749 bd = 2;
752 if (tPtr->flags.secure) {
753 text = makeHiddenString(strlen(tPtr->text));
754 } else {
755 text = tPtr->text;
758 totalWidth = tPtr->view->size.width - 2*bd;
760 drawbuffer = XCreatePixmap(screen->display, view->window,
761 view->size.width, view->size.height, screen->depth);
762 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
763 0,0, view->size.width,view->size.height);
764 /* this is quite dirty */
765 viewbuffer.screen = view->screen;
766 viewbuffer.size = view->size;
767 viewbuffer.window = drawbuffer;
770 if (tPtr->textLen > 0) {
771 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
772 tPtr->textLen - tPtr->viewPosition);
774 th = WMFontHeight(tPtr->font);
776 ty = tPtr->offsetWidth;
777 switch (tPtr->flags.alignment) {
778 case WALeft:
779 tx = tPtr->offsetWidth + 1;
780 if (tw < tPtr->usableWidth)
781 XFillRectangle(screen->display, drawbuffer,
782 WMColorGC(screen->white),
783 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
784 break;
786 case WACenter:
787 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
788 if (tw < tPtr->usableWidth)
789 XClearArea(screen->display, view->window, bd, bd,
790 totalWidth, view->size.height-2*bd, False);
791 break;
793 default:
794 case WARight:
795 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
796 if (tw < tPtr->usableWidth)
797 XClearArea(screen->display, view->window, bd, bd,
798 totalWidth-tw, view->size.height-2*bd, False);
799 break;
802 if (!tPtr->flags.enabled)
803 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
805 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
806 tPtr->font, tx, ty,
807 &(text[tPtr->viewPosition]),
808 tPtr->textLen - tPtr->viewPosition);
810 if (tPtr->selection.count) {
811 int count,count2;
813 count = tPtr->selection.count < 0
814 ? tPtr->selection.position + tPtr->selection.count
815 : tPtr->selection.position;
816 count2 = abs(tPtr->selection.count);
817 if (count < tPtr->viewPosition) {
818 count2 = abs(count2 - abs(tPtr->viewPosition - count));
819 count = tPtr->viewPosition;
823 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
824 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
826 XSetBackground(screen->display, screen->textFieldGC,
827 screen->gray->color.pixel);
829 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
830 tPtr->font, rx, ty, &(text[count]),
831 count2);
833 XSetBackground(screen->display, screen->textFieldGC,
834 screen->white->color.pixel);
837 if (!tPtr->flags.enabled)
838 WMSetColorInGC(screen->black, screen->textFieldGC);
839 } else {
840 XFillRectangle(screen->display, drawbuffer,
841 WMColorGC(screen->white),
842 bd,bd, totalWidth,view->size.height-2*bd);
845 /* draw relief */
846 if (tPtr->flags.bordered) {
847 drawRelief(&viewbuffer, tPtr->flags.beveled);
850 if (tPtr->flags.secure)
851 free(text);
852 XCopyArea(screen->display, drawbuffer, view->window,
853 screen->copyGC, 0,0, view->size.width,
854 view->size.height,0,0);
855 XFreePixmap(screen->display, drawbuffer);
857 /* draw cursor */
858 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
859 paintCursor(tPtr);
864 #if 0
865 static void
866 blinkCursor(void *data)
868 TextField *tPtr = (TextField*)data;
870 if (tPtr->flags.cursorOn) {
871 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
872 data);
873 } else {
874 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
875 data);
877 paintCursor(tPtr);
878 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
880 #endif
883 static void
884 handleEvents(XEvent *event, void *data)
886 TextField *tPtr = (TextField*)data;
888 CHECK_CLASS(data, WC_TextField);
891 switch (event->type) {
892 case FocusIn:
893 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
894 return;
895 tPtr->flags.focused = 1;
896 #if 0
897 if (!tPtr->timerID) {
898 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
899 blinkCursor, tPtr);
901 #endif
902 paintTextField(tPtr);
904 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
906 tPtr->flags.notIllegalMovement = 0;
907 break;
909 case FocusOut:
910 tPtr->flags.focused = 0;
911 #if 0
912 if (tPtr->timerID)
913 WMDeleteTimerHandler(tPtr->timerID);
914 tPtr->timerID = NULL;
915 #endif
917 paintTextField(tPtr);
918 if (!tPtr->flags.notIllegalMovement) {
919 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
920 (void*)WMIllegalTextMovement);
922 break;
924 case Expose:
925 if (event->xexpose.count!=0)
926 break;
927 paintTextField(tPtr);
928 break;
930 case DestroyNotify:
931 destroyTextField(tPtr);
932 break;
937 static void
938 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
940 char buffer[64];
941 KeySym ksym;
942 int count, refresh = 0;
943 int control_pressed = 0;
945 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
946 control_pressed = 1;
949 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
950 buffer[count] = '\0';
952 if (!(event->xkey.state & ShiftMask)) {
953 if (tPtr->selection.count)
954 refresh = 1;
955 tPtr->prevselection = tPtr->selection;
956 tPtr->selection.position = tPtr->cursorPosition;
957 tPtr->selection.count = 0;
960 /* Be careful in any case in this switch statement, never to call
961 * to more than a function that can generate text change notifications.
962 * Only one text change notification should be sent in any case.
963 * Else hazardous things can happen.
964 * Maybe we need a better solution than the function wrapper to inform
965 * functions that change text in text fields, if they need to send a
966 * change notification or not. -Dan
968 switch (ksym) {
969 case XK_Tab:
970 #ifdef XK_ISO_Left_Tab
971 case XK_ISO_Left_Tab:
972 #endif
973 if (event->xkey.state & ShiftMask) {
974 if (tPtr->view->prevFocusChain) {
975 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
976 tPtr->view->prevFocusChain);
977 tPtr->flags.notIllegalMovement = 1;
979 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
980 (void*)WMBacktabTextMovement);
981 } else {
982 if (tPtr->view->nextFocusChain) {
983 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
984 tPtr->view->nextFocusChain);
985 tPtr->flags.notIllegalMovement = 1;
987 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
988 (void*)WMTabTextMovement);
990 break;
992 case XK_Return:
993 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
994 (void*)WMReturnTextMovement);
995 break;
997 case WM_EMACSKEY_LEFT:
998 if (!control_pressed) {
999 goto normal_key;
1001 #ifdef XK_KP_Left
1002 case XK_KP_Left:
1003 #endif
1004 case XK_Left:
1005 if (tPtr->cursorPosition > 0) {
1006 paintCursor(tPtr);
1007 if (event->xkey.state & ControlMask) {
1008 int i;
1009 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
1010 if (tPtr->text[i] == ' ' || i == 0) {
1011 tPtr->cursorPosition = i;
1012 break;
1014 } else {
1015 tPtr->cursorPosition--;
1017 if (tPtr->cursorPosition < tPtr->viewPosition) {
1018 tPtr->viewPosition = tPtr->cursorPosition;
1019 refresh = 1;
1020 } else {
1021 paintCursor(tPtr);
1024 break;
1026 case WM_EMACSKEY_RIGHT:
1027 if (!control_pressed) {
1028 goto normal_key;
1030 #ifdef XK_KP_Right
1031 case XK_KP_Right:
1032 #endif
1033 case XK_Right:
1034 if (tPtr->cursorPosition < tPtr->textLen) {
1035 paintCursor(tPtr);
1036 if (event->xkey.state & ControlMask) {
1037 int i;
1038 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
1039 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
1040 tPtr->cursorPosition = i;
1041 break;
1043 } else {
1044 tPtr->cursorPosition++;
1046 while (WMWidthOfString(tPtr->font,
1047 &(tPtr->text[tPtr->viewPosition]),
1048 tPtr->cursorPosition-tPtr->viewPosition)
1049 > tPtr->usableWidth) {
1050 tPtr->viewPosition++;
1051 refresh = 1;
1053 if (!refresh)
1054 paintCursor(tPtr);
1056 break;
1058 case WM_EMACSKEY_HOME:
1059 if (!control_pressed) {
1060 goto normal_key;
1062 #ifdef XK_KP_Home
1063 case XK_KP_Home:
1064 #endif
1065 case XK_Home:
1066 if (tPtr->cursorPosition > 0) {
1067 paintCursor(tPtr);
1068 tPtr->cursorPosition = 0;
1069 if (tPtr->viewPosition > 0) {
1070 tPtr->viewPosition = 0;
1071 refresh = 1;
1072 } else {
1073 paintCursor(tPtr);
1076 break;
1078 case WM_EMACSKEY_END:
1079 if (!control_pressed) {
1080 goto normal_key;
1082 #ifdef XK_KP_End
1083 case XK_KP_End:
1084 #endif
1085 case XK_End:
1086 if (tPtr->cursorPosition < tPtr->textLen) {
1087 paintCursor(tPtr);
1088 tPtr->cursorPosition = tPtr->textLen;
1089 tPtr->viewPosition = 0;
1090 while (WMWidthOfString(tPtr->font,
1091 &(tPtr->text[tPtr->viewPosition]),
1092 tPtr->textLen-tPtr->viewPosition)
1093 > tPtr->usableWidth) {
1094 tPtr->viewPosition++;
1095 refresh = 1;
1097 if (!refresh)
1098 paintCursor(tPtr);
1100 break;
1102 case WM_EMACSKEY_BS:
1103 if (!control_pressed) {
1104 goto normal_key;
1106 case XK_BackSpace:
1107 if (tPtr->cursorPosition > 0) {
1108 WMRange range;
1110 if (tPtr->prevselection.count) {
1111 range.position = tPtr->prevselection.count < 0
1112 ? tPtr->prevselection.position + tPtr->prevselection.count
1113 : tPtr->prevselection.position;
1115 range.count = abs(tPtr->prevselection.count);
1116 } else {
1117 range.position = tPtr->cursorPosition - 1;
1118 range.count = 1;
1120 WMDeleteTextFieldRange(tPtr, range);
1121 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1122 (void*)WMDeleteTextEvent);
1124 break;
1126 case WM_EMACSKEY_DEL:
1127 if (!control_pressed) {
1128 goto normal_key;
1130 #ifdef XK_KP_Delete
1131 case XK_KP_Delete:
1132 #endif
1133 case XK_Delete:
1134 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1135 WMRange range;
1137 if (tPtr->prevselection.count) {
1138 range.position = tPtr->prevselection.count < 0
1139 ? tPtr->prevselection.position + tPtr->prevselection.count
1140 : tPtr->prevselection.position;
1142 range.count = abs(tPtr->prevselection.count);
1143 } else {
1144 range.position = tPtr->cursorPosition;
1145 range.count = 1;
1147 WMDeleteTextFieldRange(tPtr, range);
1148 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1149 (void*)WMDeleteTextEvent);
1151 break;
1153 normal_key:
1154 default:
1155 if (count > 0 && !iscntrl(buffer[0])) {
1156 WMRange range;
1158 if (tPtr->prevselection.count) {
1159 range.position = tPtr->prevselection.count < 0
1160 ? tPtr->prevselection.position + tPtr->prevselection.count
1161 : tPtr->prevselection.position;
1163 range.count = abs(tPtr->prevselection.count);
1164 } else {
1165 range.position = tPtr->cursorPosition;
1166 range.count = 1;
1168 if (tPtr->prevselection.count)
1169 WMDeleteTextFieldRange(tPtr, range);
1170 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1171 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1172 (void*)WMInsertTextEvent);
1173 } else {
1174 return;
1176 break;
1178 if (event->xkey.state & ShiftMask) {
1179 if (tPtr->selection.count == 0)
1180 tPtr->selection.position = tPtr->cursorPosition;
1181 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1182 refresh = 1;
1184 tPtr->prevselection.count = 0;
1185 if (refresh) {
1186 paintTextField(tPtr);
1191 static int
1192 pointToCursorPosition(TextField *tPtr, int x)
1194 int a, b, mid;
1195 int tw;
1197 if (tPtr->flags.bordered)
1198 x -= 2;
1200 a = tPtr->viewPosition;
1201 b = tPtr->viewPosition + tPtr->textLen;
1202 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1203 tPtr->textLen - tPtr->viewPosition) < x)
1204 return tPtr->textLen;
1206 while (a < b && b-a>1) {
1207 mid = (a+b)/2;
1208 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1209 mid - tPtr->viewPosition);
1210 if (tw > x)
1211 b = mid;
1212 else if (tw < x)
1213 a = mid;
1214 else
1215 return mid;
1217 return (a+b)/2;
1221 static void
1222 handleTextFieldActionEvents(XEvent *event, void *data)
1224 TextField *tPtr = (TextField*)data;
1225 static int move;
1227 CHECK_CLASS(data, WC_TextField);
1229 switch (event->type) {
1230 case KeyPress:
1231 if (tPtr->flags.enabled && tPtr->flags.focused) {
1232 handleTextFieldKeyPress(tPtr, event);
1233 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1234 W_VIEW(tPtr)->window, False,
1235 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1236 GrabModeAsync, GrabModeAsync, None,
1237 W_VIEW(tPtr)->screen->invisibleCursor,
1238 CurrentTime);
1239 tPtr->flags.pointerGrabbed = 1;
1241 break;
1243 case MotionNotify:
1245 if (tPtr->flags.pointerGrabbed) {
1246 tPtr->flags.pointerGrabbed = 0;
1247 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1250 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1252 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1253 tPtr->usableWidth) {
1254 if (WMWidthOfString(tPtr->font,
1255 &(tPtr->text[tPtr->viewPosition]),
1256 tPtr->cursorPosition-tPtr->viewPosition)
1257 > tPtr->usableWidth) {
1258 tPtr->viewPosition++;
1260 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1261 paintCursor(tPtr);
1262 tPtr->viewPosition--;
1265 if (!tPtr->selection.count) {
1266 tPtr->selection.position = tPtr->cursorPosition;
1269 tPtr->cursorPosition =
1270 pointToCursorPosition(tPtr, event->xmotion.x);
1272 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1275 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1278 paintCursor(tPtr);
1279 paintTextField(tPtr);
1282 if (move) {
1283 XSetSelectionOwner(tPtr->view->screen->display,
1284 XA_PRIMARY, tPtr->view->window, event->xmotion.time);
1286 WMNotification *notif = WMCreateNotification("_lostOwnership",
1287 NULL,tPtr);
1288 WMPostNotification(notif);
1289 WMReleaseNotification(notif);
1292 break;
1294 case ButtonPress:
1295 if (tPtr->flags.pointerGrabbed) {
1296 tPtr->flags.pointerGrabbed = 0;
1297 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1298 break;
1301 move = 1;
1302 switch (tPtr->flags.alignment) {
1303 int textWidth;
1304 case WARight:
1305 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1306 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1307 WMSetFocusToWidget(tPtr);
1308 } else if (tPtr->flags.focused) {
1309 tPtr->selection.count = 0;
1311 if(textWidth < tPtr->usableWidth){
1312 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1313 event->xbutton.x - tPtr->usableWidth
1314 + textWidth);
1316 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1317 event->xbutton.x);
1319 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1320 event->xbutton.x);
1321 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1324 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1325 event->xbutton.x);
1327 paintTextField(tPtr);
1328 break;
1330 case WALeft:
1331 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1332 WMSetFocusToWidget(tPtr);
1333 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1334 event->xbutton.x);
1335 paintTextField(tPtr);
1336 } else if (tPtr->flags.focused) {
1337 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1338 event->xbutton.x);
1339 tPtr->selection.count = 0;
1340 paintTextField(tPtr);
1342 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1343 char *text;
1345 text = W_GetTextSelection(tPtr->view->screen, XA_PRIMARY);
1347 if (!text) {
1348 text = W_GetTextSelection(tPtr->view->screen,
1349 tPtr->view->screen->clipboardAtom);
1351 if (!text) {
1352 text = W_GetTextSelection(tPtr->view->screen,
1353 XA_CUT_BUFFER0);
1355 if (text) {
1356 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1357 XFree(text);
1358 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1359 (void*)WMInsertTextEvent);
1362 break;
1364 break;
1366 case ButtonRelease:
1367 if (tPtr->flags.pointerGrabbed) {
1368 tPtr->flags.pointerGrabbed = 0;
1369 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1372 move = 0;
1373 break;
1378 static void
1379 destroyTextField(TextField *tPtr)
1381 #if 0
1382 if (tPtr->timerID)
1383 WMDeleteTimerHandler(tPtr->timerID);
1384 #endif
1386 WMReleaseFont(tPtr->font);
1387 WMDeleteSelectionHandler(tPtr, XA_PRIMARY);
1388 WMRemoveNotificationObserver(tPtr);
1390 if (tPtr->text)
1391 free(tPtr->text);
1393 free(tPtr);