started Appearance update in WPrefs
[wmaker-crm.git] / WINGs / wtextfield.c
blob8a901b11b6dfd732720ca28d0937e587e83b2d5a
5 #include "WINGsP.h"
7 #include <X11/keysym.h>
8 #include <X11/Xatom.h>
10 #include <ctype.h>
12 #define CURSOR_BLINK_ON_DELAY 600
13 #define CURSOR_BLINK_OFF_DELAY 300
16 typedef struct WMTextFieldDelegate {
17 void *data;
19 void (*didBeginEditing)(struct WMTextFieldDelegate *self,
20 WMNotification *notif);
22 void (*didChange)(struct WMTextFieldDelegate *self,
23 WMNotification *notif);
25 void (*didEndEditing)(struct WMTextFieldDelegate *self,
26 WMNotification *notif);
28 Bool (*shouldBeginEditing)(struct WMTextFieldDelegate *self,
29 WMTextField *tPtr);
31 Bool (*shouldEndEditing)(struct WMTextFieldDelegate *self,
32 WMTextField *tPtr);
33 } WMTextFieldDelegate;
37 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
38 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
39 char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
42 typedef struct W_TextField {
43 W_Class widgetClass;
44 W_View *view;
46 #if 0
47 struct W_TextField *nextField; /* next textfield in the chain */
48 struct W_TextField *prevField;
49 #endif
51 char *text;
52 int textLen; /* size of text */
53 int bufferSize; /* memory allocated for text */
55 int viewPosition; /* position of text being shown */
57 int cursorPosition; /* position of the insertion cursor */
59 short usableWidth;
60 short offsetWidth; /* offset of text from border */
62 WMRange selection;
63 WMRange prevselection;
65 WMFont *font;
67 WMTextFieldDelegate *delegate;
69 #if 0
70 WMHandlerID timerID; /* for cursor blinking */
71 #endif
72 struct {
73 WMAlignment alignment:2;
75 unsigned int bordered:1;
77 unsigned int beveled:1;
79 unsigned int enabled:1;
81 unsigned int focused:1;
83 unsigned int cursorOn:1;
85 unsigned int secure:1; /* password entry style */
87 unsigned int pointerGrabbed:1;
89 /**/
90 unsigned int notIllegalMovement:1;
91 } flags;
92 } TextField;
95 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
96 if ((T)->delegate && (T)->delegate->C)\
97 (*(T)->delegate->C)((T)->delegate,notif);\
98 WMPostNotification(notif);\
99 WMReleaseNotification(notif);}
102 #define MIN_TEXT_BUFFER 2
103 #define TEXT_BUFFER_INCR 8
106 #define WM_EMACSKEYMASK ControlMask
108 #define WM_EMACSKEY_LEFT XK_b
109 #define WM_EMACSKEY_RIGHT XK_f
110 #define WM_EMACSKEY_HOME XK_a
111 #define WM_EMACSKEY_END XK_e
112 #define WM_EMACSKEY_BS XK_h
113 #define WM_EMACSKEY_DEL XK_d
117 #define DEFAULT_WIDTH 60
118 #define DEFAULT_HEIGHT 20
119 #define DEFAULT_BORDERED True
120 #define DEFAULT_ALIGNMENT WALeft
124 static void destroyTextField(TextField *tPtr);
125 static void paintTextField(TextField *tPtr);
127 static void handleEvents(XEvent *event, void *data);
128 static void handleTextFieldActionEvents(XEvent *event, void *data);
129 static void resizeTextField();
131 struct W_ViewProcedureTable _TextFieldViewProcedures = {
132 NULL,
133 resizeTextField,
134 NULL
138 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
139 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
141 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
142 &((tPtr)->text[(start)]), (end) - (start) + 1))
145 static void
146 memmv(char *dest, char *src, int size)
148 int i;
150 if (dest > src) {
151 for (i=size-1; i>=0; i--) {
152 dest[i] = src[i];
154 } else if (dest < src) {
155 for (i=0; i<size; i++) {
156 dest[i] = src[i];
162 static int
163 incrToFit(TextField *tPtr)
165 int vp = tPtr->viewPosition;
167 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
168 tPtr->viewPosition++;
170 return vp!=tPtr->viewPosition;
174 static int
175 incrToFit2(TextField *tPtr)
177 int vp = tPtr->viewPosition;
178 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
179 >= tPtr->usableWidth)
180 tPtr->viewPosition++;
183 return vp!=tPtr->viewPosition;
187 static void
188 decrToFit(TextField *tPtr)
190 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
191 && tPtr->viewPosition>0)
192 tPtr->viewPosition--;
195 #undef TEXT_WIDTH
196 #undef TEXT_WIDTH2
199 WMTextField*
200 WMCreateTextField(WMWidget *parent)
202 TextField *tPtr;
204 tPtr = wmalloc(sizeof(TextField));
205 memset(tPtr, 0, sizeof(TextField));
207 tPtr->widgetClass = WC_TextField;
209 tPtr->view = W_CreateView(W_VIEW(parent));
210 if (!tPtr->view) {
211 free(tPtr);
212 return NULL;
214 tPtr->view->self = tPtr;
216 tPtr->view->attribFlags |= CWCursor;
217 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
219 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
221 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
222 tPtr->text[0] = 0;
223 tPtr->textLen = 0;
224 tPtr->bufferSize = MIN_TEXT_BUFFER;
226 tPtr->flags.enabled = 1;
228 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
229 |FocusChangeMask, handleEvents, tPtr);
231 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
233 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
235 tPtr->flags.bordered = DEFAULT_BORDERED;
236 tPtr->flags.beveled = True;
237 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
238 tPtr->offsetWidth =
239 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
241 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
242 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
243 handleTextFieldActionEvents, tPtr);
245 tPtr->flags.cursorOn = 1;
247 return tPtr;
251 void
252 WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
254 tPtr->delegate = delegate;
258 void
259 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
261 int len;
263 CHECK_CLASS(tPtr, WC_TextField);
265 if (!text)
266 return;
268 len = strlen(text);
270 /* check if buffer will hold the text */
271 if (len + tPtr->textLen >= tPtr->bufferSize) {
272 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
273 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
276 if (position < 0 || position >= tPtr->textLen) {
277 /* append the text at the end */
278 strcat(tPtr->text, text);
280 incrToFit(tPtr);
282 tPtr->textLen += len;
283 tPtr->cursorPosition += len;
284 } else {
285 /* insert text at position */
286 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
287 tPtr->textLen-position+1);
289 memcpy(&(tPtr->text[position]), text, len);
291 tPtr->textLen += len;
292 if (position >= tPtr->cursorPosition) {
293 tPtr->cursorPosition += len;
294 incrToFit2(tPtr);
295 } else {
296 incrToFit(tPtr);
300 paintTextField(tPtr);
302 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
303 (void*)WMInsertTextEvent);
307 static void
308 deleteTextFieldRange(WMTextField *tPtr, WMRange range)
310 CHECK_CLASS(tPtr, WC_TextField);
312 if (range.position >= tPtr->textLen)
313 return;
315 if (range.count < 1) {
316 if (range.position < 0)
317 range.position = 0;
318 tPtr->text[range.position] = 0;
319 tPtr->textLen = range.position;
321 tPtr->cursorPosition = 0;
322 tPtr->viewPosition = 0;
323 } else {
324 if (range.position + range.count > tPtr->textLen)
325 range.count = tPtr->textLen - range.position;
326 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
327 tPtr->textLen - (range.position+range.count) + 1);
328 tPtr->textLen -= range.count;
330 if (tPtr->cursorPosition > range.position)
331 tPtr->cursorPosition -= range.count;
333 decrToFit(tPtr);
336 paintTextField(tPtr);
340 void
341 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
343 deleteTextFieldRange(tPtr, range);
344 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
345 (void*)WMDeleteTextEvent);
350 char*
351 WMGetTextFieldText(WMTextField *tPtr)
353 CHECK_CLASS(tPtr, WC_TextField);
355 return wstrdup(tPtr->text);
359 void
360 WMSetTextFieldText(WMTextField *tPtr, char *text)
362 if ((text && strcmp(tPtr->text, text) == 0) ||
363 (!text && tPtr->textLen == 0))
364 return;
366 if (text==NULL) {
367 tPtr->text[0] = 0;
368 tPtr->textLen = 0;
369 } else {
370 tPtr->textLen = strlen(text);
372 if (tPtr->textLen >= tPtr->bufferSize) {
373 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
374 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
376 strcpy(tPtr->text, text);
379 if (tPtr->textLen < tPtr->cursorPosition)
380 tPtr->cursorPosition = tPtr->textLen;
382 tPtr->cursorPosition = tPtr->textLen;
383 tPtr->viewPosition = 0;
384 tPtr->selection.count = 0;
386 if (tPtr->view->flags.realized)
387 paintTextField(tPtr);
389 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
390 (void*)WMSetTextEvent);
394 void
395 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
397 /* TODO: update font change after field is mapped */
398 WMReleaseFont(tPtr->font);
399 tPtr->font = WMRetainFont(font);
403 void
404 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
406 tPtr->flags.alignment = alignment;
407 if (alignment!=WALeft) {
408 wwarning("only left alignment is supported in textfields");
409 return;
412 if (tPtr->view->flags.realized) {
413 paintTextField(tPtr);
418 void
419 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
421 tPtr->flags.bordered = bordered;
423 if (tPtr->view->flags.realized) {
424 paintTextField(tPtr);
429 void
430 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
432 tPtr->flags.beveled = flag;
434 if (tPtr->view->flags.realized) {
435 paintTextField(tPtr);
441 void
442 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
444 tPtr->flags.secure = flag;
446 if (tPtr->view->flags.realized) {
447 paintTextField(tPtr);
452 Bool
453 WMGetTextFieldEditable(WMTextField *tPtr)
455 return tPtr->flags.enabled;
459 void
460 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
462 tPtr->flags.enabled = flag;
464 if (tPtr->view->flags.realized) {
465 paintTextField(tPtr);
470 void
471 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
473 if (tPtr->flags.enabled) {
474 if (range.position < 0) {
475 range.count += range.position;
476 range.count = (range.count < 0) ? 0 : range.count;
477 range.position = 0;
478 } else if (range.position > tPtr->textLen) {
479 range.position = tPtr->textLen;
480 range.count = 0;
483 if (range.position + range.count > tPtr->textLen)
484 range.count = tPtr->textLen - range.position;
486 tPtr->prevselection = tPtr->selection; /* check if this is needed */
488 tPtr->selection = range;
490 if (tPtr->view->flags.realized) {
491 paintTextField(tPtr);
497 void
498 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
500 if (tPtr->flags.enabled) {
501 if (position > tPtr->textLen)
502 position = tPtr->textLen;
504 tPtr->cursorPosition = position;
505 if (tPtr->view->flags.realized) {
506 paintTextField(tPtr);
512 void
513 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
515 CHECK_CLASS(tPtr, WC_TextField);
516 if (next == NULL) {
517 if (tPtr->view->nextFocusChain)
518 tPtr->view->nextFocusChain->prevFocusChain = NULL;
519 tPtr->view->nextFocusChain = NULL;
520 return;
523 CHECK_CLASS(next, WC_TextField);
525 if (tPtr->view->nextFocusChain)
526 tPtr->view->nextFocusChain->prevFocusChain = NULL;
527 if (next->view->prevFocusChain)
528 next->view->prevFocusChain->nextFocusChain = NULL;
530 tPtr->view->nextFocusChain = next->view;
531 next->view->prevFocusChain = tPtr->view;
535 void
536 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
538 CHECK_CLASS(tPtr, WC_TextField);
539 if (prev == NULL) {
540 if (tPtr->view->prevFocusChain)
541 tPtr->view->prevFocusChain->nextFocusChain = NULL;
542 tPtr->view->prevFocusChain = NULL;
543 return;
546 CHECK_CLASS(prev, WC_TextField);
548 if (tPtr->view->prevFocusChain)
549 tPtr->view->prevFocusChain->nextFocusChain = NULL;
550 if (prev->view->nextFocusChain)
551 prev->view->nextFocusChain->prevFocusChain = NULL;
553 tPtr->view->prevFocusChain = prev->view;
554 prev->view->nextFocusChain = tPtr->view;
558 static void
559 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
561 W_ResizeView(tPtr->view, width, height);
563 tPtr->offsetWidth =
564 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
566 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
570 static char*
571 makeHiddenString(int length)
573 char *data = wmalloc(length+1);
575 memset(data, '*', length);
576 data[length] = '\0';
577 return data;
581 static void
582 paintCursor(TextField *tPtr)
584 int cx;
585 WMScreen *screen = tPtr->view->screen;
586 int textWidth;
587 char *text;
589 if (tPtr->flags.secure)
590 text = makeHiddenString(strlen(tPtr->text));
591 else
592 text = tPtr->text;
594 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
595 tPtr->cursorPosition-tPtr->viewPosition);
597 switch (tPtr->flags.alignment) {
598 case WARight:
599 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
600 if (textWidth < tPtr->usableWidth)
601 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
602 else
603 cx += tPtr->offsetWidth + 1;
604 break;
605 case WALeft:
606 cx += tPtr->offsetWidth + 1;
607 break;
608 case WAJustified:
609 /* not supported */
610 case WACenter:
611 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
612 if (textWidth < tPtr->usableWidth)
613 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
614 else
615 cx += tPtr->offsetWidth;
616 break;
619 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
620 cx, tPtr->offsetWidth, 1,
621 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
622 printf("%d %d\n",cx,tPtr->cursorPosition);
624 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
625 cx, tPtr->offsetWidth, cx,
626 tPtr->view->size.height - tPtr->offsetWidth - 1);
628 if (tPtr->flags.secure)
629 free(text);
634 static void
635 drawRelief(WMView *view, Bool beveled)
637 WMScreen *scr = view->screen;
638 Display *dpy = scr->display;
639 GC wgc;
640 GC lgc;
641 GC dgc;
642 int width = view->size.width;
643 int height = view->size.height;
645 dgc = WMColorGC(scr->darkGray);
647 if (!beveled) {
648 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
650 return;
652 wgc = WMColorGC(scr->white);
653 lgc = WMColorGC(scr->gray);
655 /* top left */
656 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
657 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
659 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
660 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
662 /* bottom right */
663 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
664 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
666 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
667 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
671 static void
672 paintTextField(TextField *tPtr)
674 W_Screen *screen = tPtr->view->screen;
675 W_View *view = tPtr->view;
676 W_View viewbuffer;
677 int tx, ty, tw, th;
678 int rx;
679 int bd;
680 int totalWidth;
681 char *text;
682 Pixmap drawbuffer;
685 if (!view->flags.realized || !view->flags.mapped)
686 return;
688 if (!tPtr->flags.bordered) {
689 bd = 0;
690 } else {
691 bd = 2;
694 if (tPtr->flags.secure) {
695 text = makeHiddenString(strlen(tPtr->text));
696 } else {
697 text = tPtr->text;
700 totalWidth = tPtr->view->size.width - 2*bd;
702 drawbuffer = XCreatePixmap(screen->display, view->window,
703 view->size.width, view->size.height, screen->depth);
704 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
705 0,0, view->size.width,view->size.height);
706 /* this is quite dirty */
707 viewbuffer.screen = view->screen;
708 viewbuffer.size = view->size;
709 viewbuffer.window = drawbuffer;
712 if (tPtr->textLen > 0) {
713 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
714 tPtr->textLen - tPtr->viewPosition);
716 th = WMFontHeight(tPtr->font);
718 ty = tPtr->offsetWidth;
719 switch (tPtr->flags.alignment) {
720 case WALeft:
721 tx = tPtr->offsetWidth + 1;
722 if (tw < tPtr->usableWidth)
723 XFillRectangle(screen->display, drawbuffer,
724 WMColorGC(screen->white),
725 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
726 break;
728 case WACenter:
729 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
730 if (tw < tPtr->usableWidth)
731 XClearArea(screen->display, view->window, bd, bd,
732 totalWidth, view->size.height-2*bd, False);
733 break;
735 default:
736 case WARight:
737 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
738 if (tw < tPtr->usableWidth)
739 XClearArea(screen->display, view->window, bd, bd,
740 totalWidth-tw, view->size.height-2*bd, False);
741 break;
744 if (!tPtr->flags.enabled)
745 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
747 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
748 tPtr->font, tx, ty,
749 &(text[tPtr->viewPosition]),
750 tPtr->textLen - tPtr->viewPosition);
752 if (tPtr->selection.count) {
753 int count,count2;
755 count = tPtr->selection.count < 0
756 ? tPtr->selection.position + tPtr->selection.count
757 : tPtr->selection.position;
758 count2 = abs(tPtr->selection.count);
759 if (count < tPtr->viewPosition) {
760 count2 = abs(count2 - abs(tPtr->viewPosition - count));
761 count = tPtr->viewPosition;
765 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
766 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
768 XSetBackground(screen->display, screen->textFieldGC,
769 screen->gray->color.pixel);
771 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
772 tPtr->font, rx, ty, &(text[count]),
773 count2);
775 XSetBackground(screen->display, screen->textFieldGC,
776 screen->white->color.pixel);
779 if (!tPtr->flags.enabled)
780 WMSetColorInGC(screen->black, screen->textFieldGC);
781 } else {
782 XFillRectangle(screen->display, drawbuffer,
783 WMColorGC(screen->white),
784 bd,bd, totalWidth,view->size.height-2*bd);
787 /* draw relief */
788 if (tPtr->flags.bordered) {
789 drawRelief(&viewbuffer, tPtr->flags.beveled);
792 if (tPtr->flags.secure)
793 free(text);
794 XCopyArea(screen->display, drawbuffer, view->window,
795 screen->copyGC, 0,0, view->size.width,
796 view->size.height,0,0);
797 XFreePixmap(screen->display, drawbuffer);
799 /* draw cursor */
800 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
801 paintCursor(tPtr);
806 #if 0
807 static void
808 blinkCursor(void *data)
810 TextField *tPtr = (TextField*)data;
812 if (tPtr->flags.cursorOn) {
813 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
814 data);
815 } else {
816 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
817 data);
819 paintCursor(tPtr);
820 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
822 #endif
825 static void
826 handleEvents(XEvent *event, void *data)
828 TextField *tPtr = (TextField*)data;
830 CHECK_CLASS(data, WC_TextField);
833 switch (event->type) {
834 case FocusIn:
835 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
836 return;
837 tPtr->flags.focused = 1;
838 #if 0
839 if (!tPtr->timerID) {
840 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
841 blinkCursor, tPtr);
843 #endif
844 paintTextField(tPtr);
846 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
848 tPtr->flags.notIllegalMovement = 0;
849 break;
851 case FocusOut:
852 tPtr->flags.focused = 0;
853 #if 0
854 if (tPtr->timerID)
855 WMDeleteTimerHandler(tPtr->timerID);
856 tPtr->timerID = NULL;
857 #endif
859 paintTextField(tPtr);
860 if (!tPtr->flags.notIllegalMovement) {
861 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
862 (void*)WMIllegalTextMovement);
864 break;
866 case Expose:
867 if (event->xexpose.count!=0)
868 break;
869 paintTextField(tPtr);
870 break;
872 case DestroyNotify:
873 destroyTextField(tPtr);
874 break;
879 static void
880 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
882 char buffer[64];
883 KeySym ksym;
884 int count, refresh = 0;
885 int control_pressed = 0;
887 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
888 control_pressed = 1;
891 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
892 buffer[count] = '\0';
894 if (!(event->xkey.state & ShiftMask)) {
895 if (tPtr->selection.count)
896 refresh = 1;
897 tPtr->prevselection = tPtr->selection;
898 tPtr->selection.position = tPtr->cursorPosition;
899 tPtr->selection.count = 0;
902 /* Be careful in any case in this switch statement, never to call
903 * to more than a function that can generate text change notifications.
904 * Only one text change notification should be sent in any case.
905 * Else hazardous things can happen.
906 * Maybe we need a better solution than the function wrapper to inform
907 * functions that change text in text fields, if they need to send a
908 * change notification or not. -Dan
910 switch (ksym) {
911 case XK_Tab:
912 case XK_ISO_Left_Tab:
913 if (event->xkey.state & ShiftMask) {
914 if (tPtr->view->prevFocusChain) {
915 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
916 tPtr->view->prevFocusChain);
917 tPtr->flags.notIllegalMovement = 1;
919 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
920 (void*)WMBacktabTextMovement);
921 } else {
922 if (tPtr->view->nextFocusChain) {
923 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
924 tPtr->view->nextFocusChain);
925 tPtr->flags.notIllegalMovement = 1;
927 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
928 (void*)WMTabTextMovement);
930 break;
932 case XK_Return:
933 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
934 (void*)WMReturnTextMovement);
935 break;
937 case WM_EMACSKEY_LEFT:
938 if (!control_pressed) {
939 goto normal_key;
941 case XK_KP_Left:
942 case XK_Left:
943 if (tPtr->cursorPosition > 0) {
944 paintCursor(tPtr);
945 if (event->xkey.state & ControlMask) {
946 int i;
947 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
948 if (tPtr->text[i] == ' ' || i == 0) {
949 tPtr->cursorPosition = i;
950 break;
952 } else {
953 tPtr->cursorPosition--;
955 if (tPtr->cursorPosition < tPtr->viewPosition) {
956 tPtr->viewPosition = tPtr->cursorPosition;
957 refresh = 1;
958 } else {
959 paintCursor(tPtr);
962 break;
964 case WM_EMACSKEY_RIGHT:
965 if (!control_pressed) {
966 goto normal_key;
968 case XK_KP_Right:
969 case XK_Right:
970 if (tPtr->cursorPosition < tPtr->textLen) {
971 paintCursor(tPtr);
972 if (event->xkey.state & ControlMask) {
973 int i;
974 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
975 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
976 tPtr->cursorPosition = i;
977 break;
979 } else {
980 tPtr->cursorPosition++;
982 while (WMWidthOfString(tPtr->font,
983 &(tPtr->text[tPtr->viewPosition]),
984 tPtr->cursorPosition-tPtr->viewPosition)
985 > tPtr->usableWidth) {
986 tPtr->viewPosition++;
987 refresh = 1;
989 if (!refresh)
990 paintCursor(tPtr);
992 break;
994 case WM_EMACSKEY_HOME:
995 if (!control_pressed) {
996 goto normal_key;
998 case XK_KP_Home:
999 case XK_Home:
1000 if (tPtr->cursorPosition > 0) {
1001 paintCursor(tPtr);
1002 tPtr->cursorPosition = 0;
1003 if (tPtr->viewPosition > 0) {
1004 tPtr->viewPosition = 0;
1005 refresh = 1;
1006 } else {
1007 paintCursor(tPtr);
1010 break;
1012 case WM_EMACSKEY_END:
1013 if (!control_pressed) {
1014 goto normal_key;
1016 case XK_KP_End:
1017 case XK_End:
1018 if (tPtr->cursorPosition < tPtr->textLen) {
1019 paintCursor(tPtr);
1020 tPtr->cursorPosition = tPtr->textLen;
1021 tPtr->viewPosition = 0;
1022 while (WMWidthOfString(tPtr->font,
1023 &(tPtr->text[tPtr->viewPosition]),
1024 tPtr->textLen-tPtr->viewPosition)
1025 > tPtr->usableWidth) {
1026 tPtr->viewPosition++;
1027 refresh = 1;
1029 if (!refresh)
1030 paintCursor(tPtr);
1032 break;
1034 case WM_EMACSKEY_BS:
1035 if (!control_pressed) {
1036 goto normal_key;
1038 case XK_BackSpace:
1039 if (tPtr->cursorPosition > 0) {
1040 WMRange range;
1042 if (tPtr->prevselection.count) {
1043 range.position = tPtr->prevselection.count < 0
1044 ? tPtr->prevselection.position + tPtr->prevselection.count
1045 : tPtr->prevselection.position;
1047 range.count = abs(tPtr->prevselection.count);
1048 } else {
1049 range.position = tPtr->cursorPosition - 1;
1050 range.count = 1;
1052 WMDeleteTextFieldRange(tPtr, range);
1054 break;
1056 case WM_EMACSKEY_DEL:
1057 if (!control_pressed) {
1058 goto normal_key;
1060 case XK_KP_Delete:
1061 case XK_Delete:
1062 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1063 WMRange range;
1065 if (tPtr->prevselection.count) {
1066 range.position = tPtr->prevselection.count < 0
1067 ? tPtr->prevselection.position + tPtr->prevselection.count
1068 : tPtr->prevselection.position;
1070 range.count = abs(tPtr->prevselection.count);
1071 } else {
1072 range.position = tPtr->cursorPosition;
1073 range.count = 1;
1075 WMDeleteTextFieldRange(tPtr, range);
1077 break;
1079 normal_key:
1080 default:
1081 if (count > 0 && !iscntrl(buffer[0])) {
1082 WMRange range;
1084 if (tPtr->prevselection.count) {
1085 range.position = tPtr->prevselection.count < 0
1086 ? tPtr->prevselection.position + tPtr->prevselection.count
1087 : tPtr->prevselection.position;
1089 range.count = abs(tPtr->prevselection.count);
1090 } else {
1091 range.position = tPtr->cursorPosition;
1092 range.count = 1;
1094 if (tPtr->prevselection.count)
1095 deleteTextFieldRange(tPtr, range);
1096 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1097 } else {
1098 return;
1100 break;
1102 if (event->xkey.state & ShiftMask) {
1103 if (tPtr->selection.count == 0)
1104 tPtr->selection.position = tPtr->cursorPosition;
1105 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1106 refresh = 1;
1108 tPtr->prevselection.count = 0;
1109 if (refresh) {
1110 paintTextField(tPtr);
1115 static int
1116 pointToCursorPosition(TextField *tPtr, int x)
1118 int a, b, mid;
1119 int tw;
1121 if (tPtr->flags.bordered)
1122 x -= 2;
1124 a = tPtr->viewPosition;
1125 b = tPtr->viewPosition + tPtr->textLen;
1126 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1127 tPtr->textLen-tPtr->viewPosition) < x)
1128 return tPtr->textLen;
1130 while (a < b && b-a>1) {
1131 mid = (a+b)/2;
1132 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1133 mid - tPtr->viewPosition);
1134 if (tw > x)
1135 b = mid;
1136 else if (tw < x)
1137 a = mid;
1138 else
1139 return mid;
1141 return (a+b)/2;
1145 static void
1146 handleTextFieldActionEvents(XEvent *event, void *data)
1148 TextField *tPtr = (TextField*)data;
1149 static int move;
1151 CHECK_CLASS(data, WC_TextField);
1153 switch (event->type) {
1154 case KeyPress:
1155 if (tPtr->flags.enabled && tPtr->flags.focused) {
1156 handleTextFieldKeyPress(tPtr, event);
1157 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1158 W_VIEW(tPtr)->window, False,
1159 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1160 GrabModeAsync, GrabModeAsync, None,
1161 W_VIEW(tPtr)->screen->invisibleCursor,
1162 CurrentTime);
1163 tPtr->flags.pointerGrabbed = 1;
1165 break;
1167 case MotionNotify:
1169 if (tPtr->flags.pointerGrabbed) {
1170 tPtr->flags.pointerGrabbed = 0;
1171 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1174 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1176 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1177 tPtr->usableWidth) {
1178 if (WMWidthOfString(tPtr->font,
1179 &(tPtr->text[tPtr->viewPosition]),
1180 tPtr->cursorPosition-tPtr->viewPosition)
1181 > tPtr->usableWidth) {
1182 tPtr->viewPosition++;
1184 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1185 paintCursor(tPtr);
1186 tPtr->viewPosition--;
1189 if (!tPtr->selection.count) {
1190 tPtr->selection.position = tPtr->cursorPosition;
1193 tPtr->cursorPosition =
1194 pointToCursorPosition(tPtr, event->xmotion.x);
1196 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1199 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1202 paintCursor(tPtr);
1203 paintTextField(tPtr);
1206 if (move) {
1207 int count;
1208 XSetSelectionOwner(tPtr->view->screen->display,
1209 XA_PRIMARY, None, CurrentTime);
1210 count = tPtr->selection.count < 0
1211 ? tPtr->selection.position + tPtr->selection.count
1212 : tPtr->selection.position;
1213 XStoreBuffer(tPtr->view->screen->display,
1214 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1216 break;
1218 case ButtonPress:
1219 if (tPtr->flags.pointerGrabbed) {
1220 tPtr->flags.pointerGrabbed = 0;
1221 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1222 break;
1225 move = 1;
1226 switch (tPtr->flags.alignment) {
1227 int textWidth;
1228 case WARight:
1229 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1230 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1231 WMSetFocusToWidget(tPtr);
1232 } else if (tPtr->flags.focused) {
1233 tPtr->selection.count = 0;
1235 if(textWidth < tPtr->usableWidth){
1236 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1237 event->xbutton.x - tPtr->usableWidth
1238 + textWidth);
1240 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1241 event->xbutton.x);
1243 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1244 event->xbutton.x);
1245 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1248 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1249 event->xbutton.x);
1251 paintTextField(tPtr);
1252 break;
1254 case WALeft:
1255 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1256 WMSetFocusToWidget(tPtr);
1257 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1258 event->xbutton.x);
1259 paintTextField(tPtr);
1260 } else if (tPtr->flags.focused) {
1261 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1262 event->xbutton.x);
1263 tPtr->selection.count = 0;
1264 paintTextField(tPtr);
1266 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1267 char *text;
1269 text = W_GetTextSelection(tPtr->view->screen,
1270 tPtr->view->screen->clipboardAtom);
1271 if (!text) {
1272 text = W_GetTextSelection(tPtr->view->screen,
1273 XA_CUT_BUFFER0);
1275 if (text) {
1276 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1277 XFree(text);
1278 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1281 break;
1283 break;
1285 case ButtonRelease:
1286 if (tPtr->flags.pointerGrabbed) {
1287 tPtr->flags.pointerGrabbed = 0;
1288 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1291 move = 0;
1292 break;
1297 static void
1298 destroyTextField(TextField *tPtr)
1300 #if 0
1301 if (tPtr->timerID)
1302 WMDeleteTimerHandler(tPtr->timerID);
1303 #endif
1305 WMReleaseFont(tPtr->font);
1307 if (tPtr->text)
1308 free(tPtr->text);
1310 free(tPtr);