Some cleanup after the notification removal in textfields.
[wmaker-crm.git] / WINGs / wtextfield.c
blobcea839653cf65374378afe3e398e38f9f41fa00a
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 void
285 WMDeleteTextFieldRange(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);
318 char*
319 WMGetTextFieldText(WMTextField *tPtr)
321 CHECK_CLASS(tPtr, WC_TextField);
323 return wstrdup(tPtr->text);
327 void
328 WMSetTextFieldText(WMTextField *tPtr, char *text)
330 if ((text && strcmp(tPtr->text, text) == 0) ||
331 (!text && tPtr->textLen == 0))
332 return;
334 if (text==NULL) {
335 tPtr->text[0] = 0;
336 tPtr->textLen = 0;
337 } else {
338 tPtr->textLen = strlen(text);
340 if (tPtr->textLen >= tPtr->bufferSize) {
341 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
342 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
344 strcpy(tPtr->text, text);
347 if (tPtr->textLen < tPtr->cursorPosition)
348 tPtr->cursorPosition = tPtr->textLen;
350 tPtr->cursorPosition = tPtr->textLen;
351 tPtr->viewPosition = 0;
352 tPtr->selection.count = 0;
354 if (tPtr->view->flags.realized)
355 paintTextField(tPtr);
359 void
360 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
362 /* TODO: update font change after field is mapped */
363 WMReleaseFont(tPtr->font);
364 tPtr->font = WMRetainFont(font);
368 void
369 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
371 tPtr->flags.alignment = alignment;
372 if (alignment!=WALeft) {
373 wwarning("only left alignment is supported in textfields");
374 return;
377 if (tPtr->view->flags.realized) {
378 paintTextField(tPtr);
383 void
384 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
386 tPtr->flags.bordered = bordered;
388 if (tPtr->view->flags.realized) {
389 paintTextField(tPtr);
394 void
395 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
397 tPtr->flags.beveled = flag;
399 if (tPtr->view->flags.realized) {
400 paintTextField(tPtr);
406 void
407 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
409 tPtr->flags.secure = flag;
411 if (tPtr->view->flags.realized) {
412 paintTextField(tPtr);
417 Bool
418 WMGetTextFieldEditable(WMTextField *tPtr)
420 return tPtr->flags.enabled;
424 void
425 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
427 tPtr->flags.enabled = flag;
429 if (tPtr->view->flags.realized) {
430 paintTextField(tPtr);
435 void
436 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
438 if (tPtr->flags.enabled) {
439 if (range.position < 0) {
440 range.count += range.position;
441 range.count = (range.count < 0) ? 0 : range.count;
442 range.position = 0;
443 } else if (range.position > tPtr->textLen) {
444 range.position = tPtr->textLen;
445 range.count = 0;
448 if (range.position + range.count > tPtr->textLen)
449 range.count = tPtr->textLen - range.position;
451 tPtr->prevselection = tPtr->selection; /* check if this is needed */
453 tPtr->selection = range;
455 if (tPtr->view->flags.realized) {
456 paintTextField(tPtr);
462 void
463 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
465 if (tPtr->flags.enabled) {
466 if (position > tPtr->textLen)
467 position = tPtr->textLen;
469 tPtr->cursorPosition = position;
470 if (tPtr->view->flags.realized) {
471 paintTextField(tPtr);
477 void
478 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
480 CHECK_CLASS(tPtr, WC_TextField);
481 if (next == NULL) {
482 if (tPtr->view->nextFocusChain)
483 tPtr->view->nextFocusChain->prevFocusChain = NULL;
484 tPtr->view->nextFocusChain = NULL;
485 return;
488 CHECK_CLASS(next, WC_TextField);
490 if (tPtr->view->nextFocusChain)
491 tPtr->view->nextFocusChain->prevFocusChain = NULL;
492 if (next->view->prevFocusChain)
493 next->view->prevFocusChain->nextFocusChain = NULL;
495 tPtr->view->nextFocusChain = next->view;
496 next->view->prevFocusChain = tPtr->view;
500 void
501 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
503 CHECK_CLASS(tPtr, WC_TextField);
504 if (prev == NULL) {
505 if (tPtr->view->prevFocusChain)
506 tPtr->view->prevFocusChain->nextFocusChain = NULL;
507 tPtr->view->prevFocusChain = NULL;
508 return;
511 CHECK_CLASS(prev, WC_TextField);
513 if (tPtr->view->prevFocusChain)
514 tPtr->view->prevFocusChain->nextFocusChain = NULL;
515 if (prev->view->nextFocusChain)
516 prev->view->nextFocusChain->prevFocusChain = NULL;
518 tPtr->view->prevFocusChain = prev->view;
519 prev->view->nextFocusChain = tPtr->view;
523 static void
524 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
526 W_ResizeView(tPtr->view, width, height);
528 tPtr->offsetWidth =
529 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
531 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
535 static char*
536 makeHiddenString(int length)
538 char *data = wmalloc(length+1);
540 memset(data, '*', length);
541 data[length] = '\0';
542 return data;
546 static void
547 paintCursor(TextField *tPtr)
549 int cx;
550 WMScreen *screen = tPtr->view->screen;
551 int textWidth;
552 char *text;
554 if (tPtr->flags.secure)
555 text = makeHiddenString(strlen(tPtr->text));
556 else
557 text = tPtr->text;
559 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
560 tPtr->cursorPosition-tPtr->viewPosition);
562 switch (tPtr->flags.alignment) {
563 case WARight:
564 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
565 if (textWidth < tPtr->usableWidth)
566 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
567 else
568 cx += tPtr->offsetWidth + 1;
569 break;
570 case WALeft:
571 cx += tPtr->offsetWidth + 1;
572 break;
573 case WAJustified:
574 /* not supported */
575 case WACenter:
576 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
577 if (textWidth < tPtr->usableWidth)
578 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
579 else
580 cx += tPtr->offsetWidth;
581 break;
584 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
585 cx, tPtr->offsetWidth, 1,
586 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
587 printf("%d %d\n",cx,tPtr->cursorPosition);
589 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
590 cx, tPtr->offsetWidth, cx,
591 tPtr->view->size.height - tPtr->offsetWidth - 1);
593 if (tPtr->flags.secure)
594 free(text);
599 static void
600 drawRelief(WMView *view, Bool beveled)
602 WMScreen *scr = view->screen;
603 Display *dpy = scr->display;
604 GC wgc;
605 GC lgc;
606 GC dgc;
607 int width = view->size.width;
608 int height = view->size.height;
610 dgc = WMColorGC(scr->darkGray);
612 if (!beveled) {
613 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
615 return;
617 wgc = WMColorGC(scr->white);
618 lgc = WMColorGC(scr->gray);
620 /* top left */
621 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
622 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
624 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
625 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
627 /* bottom right */
628 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
629 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
631 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
632 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
636 static void
637 paintTextField(TextField *tPtr)
639 W_Screen *screen = tPtr->view->screen;
640 W_View *view = tPtr->view;
641 W_View viewbuffer;
642 int tx, ty, tw, th;
643 int rx;
644 int bd;
645 int totalWidth;
646 char *text;
647 Pixmap drawbuffer;
650 if (!view->flags.realized || !view->flags.mapped)
651 return;
653 if (!tPtr->flags.bordered) {
654 bd = 0;
655 } else {
656 bd = 2;
659 if (tPtr->flags.secure) {
660 text = makeHiddenString(strlen(tPtr->text));
661 } else {
662 text = tPtr->text;
665 totalWidth = tPtr->view->size.width - 2*bd;
667 drawbuffer = XCreatePixmap(screen->display, view->window,
668 view->size.width, view->size.height, screen->depth);
669 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
670 0,0, view->size.width,view->size.height);
671 /* this is quite dirty */
672 viewbuffer.screen = view->screen;
673 viewbuffer.size = view->size;
674 viewbuffer.window = drawbuffer;
677 if (tPtr->textLen > 0) {
678 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
679 tPtr->textLen - tPtr->viewPosition);
681 th = WMFontHeight(tPtr->font);
683 ty = tPtr->offsetWidth;
684 switch (tPtr->flags.alignment) {
685 case WALeft:
686 tx = tPtr->offsetWidth + 1;
687 if (tw < tPtr->usableWidth)
688 XFillRectangle(screen->display, drawbuffer,
689 WMColorGC(screen->white),
690 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
691 break;
693 case WACenter:
694 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
695 if (tw < tPtr->usableWidth)
696 XClearArea(screen->display, view->window, bd, bd,
697 totalWidth, view->size.height-2*bd, False);
698 break;
700 default:
701 case WARight:
702 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
703 if (tw < tPtr->usableWidth)
704 XClearArea(screen->display, view->window, bd, bd,
705 totalWidth-tw, view->size.height-2*bd, False);
706 break;
709 if (!tPtr->flags.enabled)
710 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
712 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
713 tPtr->font, tx, ty,
714 &(text[tPtr->viewPosition]),
715 tPtr->textLen - tPtr->viewPosition);
717 if (tPtr->selection.count) {
718 int count,count2;
720 count = tPtr->selection.count < 0
721 ? tPtr->selection.position + tPtr->selection.count
722 : tPtr->selection.position;
723 count2 = abs(tPtr->selection.count);
724 if (count < tPtr->viewPosition) {
725 count2 = abs(count2 - abs(tPtr->viewPosition - count));
726 count = tPtr->viewPosition;
730 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
731 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
733 XSetBackground(screen->display, screen->textFieldGC,
734 screen->gray->color.pixel);
736 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
737 tPtr->font, rx, ty, &(text[count]),
738 count2);
740 XSetBackground(screen->display, screen->textFieldGC,
741 screen->white->color.pixel);
744 if (!tPtr->flags.enabled)
745 WMSetColorInGC(screen->black, screen->textFieldGC);
746 } else {
747 XFillRectangle(screen->display, drawbuffer,
748 WMColorGC(screen->white),
749 bd,bd, totalWidth,view->size.height-2*bd);
752 /* draw relief */
753 if (tPtr->flags.bordered) {
754 drawRelief(&viewbuffer, tPtr->flags.beveled);
757 if (tPtr->flags.secure)
758 free(text);
759 XCopyArea(screen->display, drawbuffer, view->window,
760 screen->copyGC, 0,0, view->size.width,
761 view->size.height,0,0);
762 XFreePixmap(screen->display, drawbuffer);
764 /* draw cursor */
765 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
766 paintCursor(tPtr);
771 #if 0
772 static void
773 blinkCursor(void *data)
775 TextField *tPtr = (TextField*)data;
777 if (tPtr->flags.cursorOn) {
778 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
779 data);
780 } else {
781 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
782 data);
784 paintCursor(tPtr);
785 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
787 #endif
790 static void
791 handleEvents(XEvent *event, void *data)
793 TextField *tPtr = (TextField*)data;
795 CHECK_CLASS(data, WC_TextField);
798 switch (event->type) {
799 case FocusIn:
800 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
801 return;
802 tPtr->flags.focused = 1;
803 #if 0
804 if (!tPtr->timerID) {
805 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
806 blinkCursor, tPtr);
808 #endif
809 paintTextField(tPtr);
811 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
813 tPtr->flags.notIllegalMovement = 0;
814 break;
816 case FocusOut:
817 tPtr->flags.focused = 0;
818 #if 0
819 if (tPtr->timerID)
820 WMDeleteTimerHandler(tPtr->timerID);
821 tPtr->timerID = NULL;
822 #endif
824 paintTextField(tPtr);
825 if (!tPtr->flags.notIllegalMovement) {
826 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
827 (void*)WMIllegalTextMovement);
829 break;
831 case Expose:
832 if (event->xexpose.count!=0)
833 break;
834 paintTextField(tPtr);
835 break;
837 case DestroyNotify:
838 destroyTextField(tPtr);
839 break;
844 static void
845 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
847 char buffer[64];
848 KeySym ksym;
849 int count, refresh = 0;
850 int control_pressed = 0;
852 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
853 control_pressed = 1;
856 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
857 buffer[count] = '\0';
859 if (!(event->xkey.state & ShiftMask)) {
860 if (tPtr->selection.count)
861 refresh = 1;
862 tPtr->prevselection = tPtr->selection;
863 tPtr->selection.position = tPtr->cursorPosition;
864 tPtr->selection.count = 0;
867 /* Be careful in any case in this switch statement, never to call
868 * to more than a function that can generate text change notifications.
869 * Only one text change notification should be sent in any case.
870 * Else hazardous things can happen.
871 * Maybe we need a better solution than the function wrapper to inform
872 * functions that change text in text fields, if they need to send a
873 * change notification or not. -Dan
875 switch (ksym) {
876 case XK_Tab:
877 case XK_ISO_Left_Tab:
878 if (event->xkey.state & ShiftMask) {
879 if (tPtr->view->prevFocusChain) {
880 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
881 tPtr->view->prevFocusChain);
882 tPtr->flags.notIllegalMovement = 1;
884 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
885 (void*)WMBacktabTextMovement);
886 } else {
887 if (tPtr->view->nextFocusChain) {
888 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
889 tPtr->view->nextFocusChain);
890 tPtr->flags.notIllegalMovement = 1;
892 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
893 (void*)WMTabTextMovement);
895 break;
897 case XK_Return:
898 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
899 (void*)WMReturnTextMovement);
900 break;
902 case WM_EMACSKEY_LEFT:
903 if (!control_pressed) {
904 goto normal_key;
906 case XK_KP_Left:
907 case XK_Left:
908 if (tPtr->cursorPosition > 0) {
909 paintCursor(tPtr);
910 if (event->xkey.state & ControlMask) {
911 int i;
912 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
913 if (tPtr->text[i] == ' ' || i == 0) {
914 tPtr->cursorPosition = i;
915 break;
917 } else {
918 tPtr->cursorPosition--;
920 if (tPtr->cursorPosition < tPtr->viewPosition) {
921 tPtr->viewPosition = tPtr->cursorPosition;
922 refresh = 1;
923 } else {
924 paintCursor(tPtr);
927 break;
929 case WM_EMACSKEY_RIGHT:
930 if (!control_pressed) {
931 goto normal_key;
933 case XK_KP_Right:
934 case XK_Right:
935 if (tPtr->cursorPosition < tPtr->textLen) {
936 paintCursor(tPtr);
937 if (event->xkey.state & ControlMask) {
938 int i;
939 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
940 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
941 tPtr->cursorPosition = i;
942 break;
944 } else {
945 tPtr->cursorPosition++;
947 while (WMWidthOfString(tPtr->font,
948 &(tPtr->text[tPtr->viewPosition]),
949 tPtr->cursorPosition-tPtr->viewPosition)
950 > tPtr->usableWidth) {
951 tPtr->viewPosition++;
952 refresh = 1;
954 if (!refresh)
955 paintCursor(tPtr);
957 break;
959 case WM_EMACSKEY_HOME:
960 if (!control_pressed) {
961 goto normal_key;
963 case XK_KP_Home:
964 case XK_Home:
965 if (tPtr->cursorPosition > 0) {
966 paintCursor(tPtr);
967 tPtr->cursorPosition = 0;
968 if (tPtr->viewPosition > 0) {
969 tPtr->viewPosition = 0;
970 refresh = 1;
971 } else {
972 paintCursor(tPtr);
975 break;
977 case WM_EMACSKEY_END:
978 if (!control_pressed) {
979 goto normal_key;
981 case XK_KP_End:
982 case XK_End:
983 if (tPtr->cursorPosition < tPtr->textLen) {
984 paintCursor(tPtr);
985 tPtr->cursorPosition = tPtr->textLen;
986 tPtr->viewPosition = 0;
987 while (WMWidthOfString(tPtr->font,
988 &(tPtr->text[tPtr->viewPosition]),
989 tPtr->textLen-tPtr->viewPosition)
990 > tPtr->usableWidth) {
991 tPtr->viewPosition++;
992 refresh = 1;
994 if (!refresh)
995 paintCursor(tPtr);
997 break;
999 case WM_EMACSKEY_BS:
1000 if (!control_pressed) {
1001 goto normal_key;
1003 case XK_BackSpace:
1004 if (tPtr->cursorPosition > 0) {
1005 WMRange range;
1007 if (tPtr->prevselection.count) {
1008 range.position = tPtr->prevselection.count < 0
1009 ? tPtr->prevselection.position + tPtr->prevselection.count
1010 : tPtr->prevselection.position;
1012 range.count = abs(tPtr->prevselection.count);
1013 } else {
1014 range.position = tPtr->cursorPosition - 1;
1015 range.count = 1;
1017 WMDeleteTextFieldRange(tPtr, range);
1018 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1019 (void*)WMDeleteTextEvent);
1021 break;
1023 case WM_EMACSKEY_DEL:
1024 if (!control_pressed) {
1025 goto normal_key;
1027 case XK_KP_Delete:
1028 case XK_Delete:
1029 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1030 WMRange range;
1032 if (tPtr->prevselection.count) {
1033 range.position = tPtr->prevselection.count < 0
1034 ? tPtr->prevselection.position + tPtr->prevselection.count
1035 : tPtr->prevselection.position;
1037 range.count = abs(tPtr->prevselection.count);
1038 } else {
1039 range.position = tPtr->cursorPosition;
1040 range.count = 1;
1042 WMDeleteTextFieldRange(tPtr, range);
1043 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1044 (void*)WMDeleteTextEvent);
1046 break;
1048 normal_key:
1049 default:
1050 if (count > 0 && !iscntrl(buffer[0])) {
1051 WMRange range;
1053 if (tPtr->prevselection.count) {
1054 range.position = tPtr->prevselection.count < 0
1055 ? tPtr->prevselection.position + tPtr->prevselection.count
1056 : tPtr->prevselection.position;
1058 range.count = abs(tPtr->prevselection.count);
1059 } else {
1060 range.position = tPtr->cursorPosition;
1061 range.count = 1;
1063 if (tPtr->prevselection.count)
1064 WMDeleteTextFieldRange(tPtr, range);
1065 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1066 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1067 (void*)WMInsertTextEvent);
1068 } else {
1069 return;
1071 break;
1073 if (event->xkey.state & ShiftMask) {
1074 if (tPtr->selection.count == 0)
1075 tPtr->selection.position = tPtr->cursorPosition;
1076 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1077 refresh = 1;
1079 tPtr->prevselection.count = 0;
1080 if (refresh) {
1081 paintTextField(tPtr);
1086 static int
1087 pointToCursorPosition(TextField *tPtr, int x)
1089 int a, b, mid;
1090 int tw;
1092 if (tPtr->flags.bordered)
1093 x -= 2;
1095 a = tPtr->viewPosition;
1096 b = tPtr->viewPosition + tPtr->textLen;
1097 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1098 tPtr->textLen - tPtr->viewPosition) < x)
1099 return tPtr->textLen;
1101 while (a < b && b-a>1) {
1102 mid = (a+b)/2;
1103 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1104 mid - tPtr->viewPosition);
1105 if (tw > x)
1106 b = mid;
1107 else if (tw < x)
1108 a = mid;
1109 else
1110 return mid;
1112 return (a+b)/2;
1116 static void
1117 handleTextFieldActionEvents(XEvent *event, void *data)
1119 TextField *tPtr = (TextField*)data;
1120 static int move;
1122 CHECK_CLASS(data, WC_TextField);
1124 switch (event->type) {
1125 case KeyPress:
1126 if (tPtr->flags.enabled && tPtr->flags.focused) {
1127 handleTextFieldKeyPress(tPtr, event);
1128 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1129 W_VIEW(tPtr)->window, False,
1130 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1131 GrabModeAsync, GrabModeAsync, None,
1132 W_VIEW(tPtr)->screen->invisibleCursor,
1133 CurrentTime);
1134 tPtr->flags.pointerGrabbed = 1;
1136 break;
1138 case MotionNotify:
1140 if (tPtr->flags.pointerGrabbed) {
1141 tPtr->flags.pointerGrabbed = 0;
1142 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1145 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1147 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1148 tPtr->usableWidth) {
1149 if (WMWidthOfString(tPtr->font,
1150 &(tPtr->text[tPtr->viewPosition]),
1151 tPtr->cursorPosition-tPtr->viewPosition)
1152 > tPtr->usableWidth) {
1153 tPtr->viewPosition++;
1155 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1156 paintCursor(tPtr);
1157 tPtr->viewPosition--;
1160 if (!tPtr->selection.count) {
1161 tPtr->selection.position = tPtr->cursorPosition;
1164 tPtr->cursorPosition =
1165 pointToCursorPosition(tPtr, event->xmotion.x);
1167 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1170 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1173 paintCursor(tPtr);
1174 paintTextField(tPtr);
1177 if (move) {
1178 int count;
1179 XSetSelectionOwner(tPtr->view->screen->display,
1180 XA_PRIMARY, None, CurrentTime);
1181 count = tPtr->selection.count < 0
1182 ? tPtr->selection.position + tPtr->selection.count
1183 : tPtr->selection.position;
1184 XStoreBuffer(tPtr->view->screen->display,
1185 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1187 break;
1189 case ButtonPress:
1190 if (tPtr->flags.pointerGrabbed) {
1191 tPtr->flags.pointerGrabbed = 0;
1192 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1193 break;
1196 move = 1;
1197 switch (tPtr->flags.alignment) {
1198 int textWidth;
1199 case WARight:
1200 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1201 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1202 WMSetFocusToWidget(tPtr);
1203 } else if (tPtr->flags.focused) {
1204 tPtr->selection.count = 0;
1206 if(textWidth < tPtr->usableWidth){
1207 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1208 event->xbutton.x - tPtr->usableWidth
1209 + textWidth);
1211 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1212 event->xbutton.x);
1214 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1215 event->xbutton.x);
1216 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1219 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1220 event->xbutton.x);
1222 paintTextField(tPtr);
1223 break;
1225 case WALeft:
1226 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1227 WMSetFocusToWidget(tPtr);
1228 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1229 event->xbutton.x);
1230 paintTextField(tPtr);
1231 } else if (tPtr->flags.focused) {
1232 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1233 event->xbutton.x);
1234 tPtr->selection.count = 0;
1235 paintTextField(tPtr);
1237 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1238 char *text;
1240 text = W_GetTextSelection(tPtr->view->screen,
1241 tPtr->view->screen->clipboardAtom);
1242 if (!text) {
1243 text = W_GetTextSelection(tPtr->view->screen,
1244 XA_CUT_BUFFER0);
1246 if (text) {
1247 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1248 XFree(text);
1249 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1250 (void*)WMInsertTextEvent);
1253 break;
1255 break;
1257 case ButtonRelease:
1258 if (tPtr->flags.pointerGrabbed) {
1259 tPtr->flags.pointerGrabbed = 0;
1260 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1263 move = 0;
1264 break;
1269 static void
1270 destroyTextField(TextField *tPtr)
1272 #if 0
1273 if (tPtr->timerID)
1274 WMDeleteTimerHandler(tPtr->timerID);
1275 #endif
1277 WMReleaseFont(tPtr->font);
1279 if (tPtr->text)
1280 free(tPtr->text);
1282 free(tPtr);