fixed typo errors
[wmaker-crm.git] / WINGs / wtextfield.c
blob4397cb43ede9b299f2253d270a342343d3ad3568
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);
282 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
283 (void*)WMInsertTextEvent);
287 static void
288 deleteTextFieldRange(WMTextField *tPtr, WMRange range)
290 CHECK_CLASS(tPtr, WC_TextField);
292 if (range.position >= tPtr->textLen)
293 return;
295 if (range.count < 1) {
296 if (range.position < 0)
297 range.position = 0;
298 tPtr->text[range.position] = 0;
299 tPtr->textLen = range.position;
301 tPtr->cursorPosition = 0;
302 tPtr->viewPosition = 0;
303 } else {
304 if (range.position + range.count > tPtr->textLen)
305 range.count = tPtr->textLen - range.position;
306 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
307 tPtr->textLen - (range.position+range.count) + 1);
308 tPtr->textLen -= range.count;
310 if (tPtr->cursorPosition > range.position)
311 tPtr->cursorPosition -= range.count;
313 decrToFit(tPtr);
316 paintTextField(tPtr);
320 void
321 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
323 deleteTextFieldRange(tPtr, range);
324 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
325 (void*)WMDeleteTextEvent);
330 char*
331 WMGetTextFieldText(WMTextField *tPtr)
333 CHECK_CLASS(tPtr, WC_TextField);
335 return wstrdup(tPtr->text);
339 void
340 WMSetTextFieldText(WMTextField *tPtr, char *text)
342 if ((text && strcmp(tPtr->text, text) == 0) ||
343 (!text && tPtr->textLen == 0))
344 return;
346 if (text==NULL) {
347 tPtr->text[0] = 0;
348 tPtr->textLen = 0;
349 } else {
350 tPtr->textLen = strlen(text);
352 if (tPtr->textLen >= tPtr->bufferSize) {
353 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
354 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
356 strcpy(tPtr->text, text);
359 if (tPtr->textLen < tPtr->cursorPosition)
360 tPtr->cursorPosition = tPtr->textLen;
362 tPtr->cursorPosition = tPtr->textLen;
363 tPtr->viewPosition = 0;
364 tPtr->selection.count = 0;
366 if (tPtr->view->flags.realized)
367 paintTextField(tPtr);
369 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
370 (void*)WMSetTextEvent);
374 void
375 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
377 /* TODO: update font change after field is mapped */
378 WMReleaseFont(tPtr->font);
379 tPtr->font = WMRetainFont(font);
383 void
384 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
386 tPtr->flags.alignment = alignment;
387 if (alignment!=WALeft) {
388 wwarning("only left alignment is supported in textfields");
389 return;
392 if (tPtr->view->flags.realized) {
393 paintTextField(tPtr);
398 void
399 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
401 tPtr->flags.bordered = bordered;
403 if (tPtr->view->flags.realized) {
404 paintTextField(tPtr);
409 void
410 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
412 tPtr->flags.beveled = flag;
414 if (tPtr->view->flags.realized) {
415 paintTextField(tPtr);
421 void
422 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
424 tPtr->flags.secure = flag;
426 if (tPtr->view->flags.realized) {
427 paintTextField(tPtr);
432 Bool
433 WMGetTextFieldEditable(WMTextField *tPtr)
435 return tPtr->flags.enabled;
439 void
440 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
442 tPtr->flags.enabled = flag;
444 if (tPtr->view->flags.realized) {
445 paintTextField(tPtr);
450 void
451 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
453 if (tPtr->flags.enabled) {
454 if (range.position < 0) {
455 range.count += range.position;
456 range.count = (range.count < 0) ? 0 : range.count;
457 range.position = 0;
458 } else if (range.position > tPtr->textLen) {
459 range.position = tPtr->textLen;
460 range.count = 0;
463 if (range.position + range.count > tPtr->textLen)
464 range.count = tPtr->textLen - range.position;
466 tPtr->prevselection = tPtr->selection; /* check if this is needed */
468 tPtr->selection = range;
470 if (tPtr->view->flags.realized) {
471 paintTextField(tPtr);
477 void
478 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
480 if (tPtr->flags.enabled) {
481 if (position > tPtr->textLen)
482 position = tPtr->textLen;
484 tPtr->cursorPosition = position;
485 if (tPtr->view->flags.realized) {
486 paintTextField(tPtr);
492 void
493 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
495 CHECK_CLASS(tPtr, WC_TextField);
496 if (next == NULL) {
497 if (tPtr->view->nextFocusChain)
498 tPtr->view->nextFocusChain->prevFocusChain = NULL;
499 tPtr->view->nextFocusChain = NULL;
500 return;
503 CHECK_CLASS(next, WC_TextField);
505 if (tPtr->view->nextFocusChain)
506 tPtr->view->nextFocusChain->prevFocusChain = NULL;
507 if (next->view->prevFocusChain)
508 next->view->prevFocusChain->nextFocusChain = NULL;
510 tPtr->view->nextFocusChain = next->view;
511 next->view->prevFocusChain = tPtr->view;
515 void
516 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
518 CHECK_CLASS(tPtr, WC_TextField);
519 if (prev == NULL) {
520 if (tPtr->view->prevFocusChain)
521 tPtr->view->prevFocusChain->nextFocusChain = NULL;
522 tPtr->view->prevFocusChain = NULL;
523 return;
526 CHECK_CLASS(prev, WC_TextField);
528 if (tPtr->view->prevFocusChain)
529 tPtr->view->prevFocusChain->nextFocusChain = NULL;
530 if (prev->view->nextFocusChain)
531 prev->view->nextFocusChain->prevFocusChain = NULL;
533 tPtr->view->prevFocusChain = prev->view;
534 prev->view->nextFocusChain = tPtr->view;
538 static void
539 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
541 W_ResizeView(tPtr->view, width, height);
543 tPtr->offsetWidth =
544 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
546 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
550 static char*
551 makeHiddenString(int length)
553 char *data = wmalloc(length+1);
555 memset(data, '*', length);
556 data[length] = '\0';
557 return data;
561 static void
562 paintCursor(TextField *tPtr)
564 int cx;
565 WMScreen *screen = tPtr->view->screen;
566 int textWidth;
567 char *text;
569 if (tPtr->flags.secure)
570 text = makeHiddenString(strlen(tPtr->text));
571 else
572 text = tPtr->text;
574 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
575 tPtr->cursorPosition-tPtr->viewPosition);
577 switch (tPtr->flags.alignment) {
578 case WARight:
579 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
580 if (textWidth < tPtr->usableWidth)
581 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
582 else
583 cx += tPtr->offsetWidth + 1;
584 break;
585 case WALeft:
586 cx += tPtr->offsetWidth + 1;
587 break;
588 case WAJustified:
589 /* not supported */
590 case WACenter:
591 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
592 if (textWidth < tPtr->usableWidth)
593 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
594 else
595 cx += tPtr->offsetWidth;
596 break;
599 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
600 cx, tPtr->offsetWidth, 1,
601 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
602 printf("%d %d\n",cx,tPtr->cursorPosition);
604 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
605 cx, tPtr->offsetWidth, cx,
606 tPtr->view->size.height - tPtr->offsetWidth - 1);
608 if (tPtr->flags.secure)
609 free(text);
614 static void
615 drawRelief(WMView *view, Bool beveled)
617 WMScreen *scr = view->screen;
618 Display *dpy = scr->display;
619 GC wgc;
620 GC lgc;
621 GC dgc;
622 int width = view->size.width;
623 int height = view->size.height;
625 dgc = WMColorGC(scr->darkGray);
627 if (!beveled) {
628 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
630 return;
632 wgc = WMColorGC(scr->white);
633 lgc = WMColorGC(scr->gray);
635 /* top left */
636 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
637 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
639 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
640 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
642 /* bottom right */
643 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
644 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
646 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
647 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
651 static void
652 paintTextField(TextField *tPtr)
654 W_Screen *screen = tPtr->view->screen;
655 W_View *view = tPtr->view;
656 W_View viewbuffer;
657 int tx, ty, tw, th;
658 int rx;
659 int bd;
660 int totalWidth;
661 char *text;
662 Pixmap drawbuffer;
665 if (!view->flags.realized || !view->flags.mapped)
666 return;
668 if (!tPtr->flags.bordered) {
669 bd = 0;
670 } else {
671 bd = 2;
674 if (tPtr->flags.secure) {
675 text = makeHiddenString(strlen(tPtr->text));
676 } else {
677 text = tPtr->text;
680 totalWidth = tPtr->view->size.width - 2*bd;
682 drawbuffer = XCreatePixmap(screen->display, view->window,
683 view->size.width, view->size.height, screen->depth);
684 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
685 0,0, view->size.width,view->size.height);
686 /* this is quite dirty */
687 viewbuffer.screen = view->screen;
688 viewbuffer.size = view->size;
689 viewbuffer.window = drawbuffer;
692 if (tPtr->textLen > 0) {
693 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
694 tPtr->textLen - tPtr->viewPosition);
696 th = WMFontHeight(tPtr->font);
698 ty = tPtr->offsetWidth;
699 switch (tPtr->flags.alignment) {
700 case WALeft:
701 tx = tPtr->offsetWidth + 1;
702 if (tw < tPtr->usableWidth)
703 XFillRectangle(screen->display, drawbuffer,
704 WMColorGC(screen->white),
705 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
706 break;
708 case WACenter:
709 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
710 if (tw < tPtr->usableWidth)
711 XClearArea(screen->display, view->window, bd, bd,
712 totalWidth, view->size.height-2*bd, False);
713 break;
715 default:
716 case WARight:
717 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
718 if (tw < tPtr->usableWidth)
719 XClearArea(screen->display, view->window, bd, bd,
720 totalWidth-tw, view->size.height-2*bd, False);
721 break;
724 if (!tPtr->flags.enabled)
725 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
727 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
728 tPtr->font, tx, ty,
729 &(text[tPtr->viewPosition]),
730 tPtr->textLen - tPtr->viewPosition);
732 if (tPtr->selection.count) {
733 int count,count2;
735 count = tPtr->selection.count < 0
736 ? tPtr->selection.position + tPtr->selection.count
737 : tPtr->selection.position;
738 count2 = abs(tPtr->selection.count);
739 if (count < tPtr->viewPosition) {
740 count2 = abs(count2 - abs(tPtr->viewPosition - count));
741 count = tPtr->viewPosition;
745 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
746 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
748 XSetBackground(screen->display, screen->textFieldGC,
749 screen->gray->color.pixel);
751 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
752 tPtr->font, rx, ty, &(text[count]),
753 count2);
755 XSetBackground(screen->display, screen->textFieldGC,
756 screen->white->color.pixel);
759 if (!tPtr->flags.enabled)
760 WMSetColorInGC(screen->black, screen->textFieldGC);
761 } else {
762 XFillRectangle(screen->display, drawbuffer,
763 WMColorGC(screen->white),
764 bd,bd, totalWidth,view->size.height-2*bd);
767 /* draw relief */
768 if (tPtr->flags.bordered) {
769 drawRelief(&viewbuffer, tPtr->flags.beveled);
772 if (tPtr->flags.secure)
773 free(text);
774 XCopyArea(screen->display, drawbuffer, view->window,
775 screen->copyGC, 0,0, view->size.width,
776 view->size.height,0,0);
777 XFreePixmap(screen->display, drawbuffer);
779 /* draw cursor */
780 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
781 paintCursor(tPtr);
786 #if 0
787 static void
788 blinkCursor(void *data)
790 TextField *tPtr = (TextField*)data;
792 if (tPtr->flags.cursorOn) {
793 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
794 data);
795 } else {
796 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
797 data);
799 paintCursor(tPtr);
800 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
802 #endif
805 static void
806 handleEvents(XEvent *event, void *data)
808 TextField *tPtr = (TextField*)data;
810 CHECK_CLASS(data, WC_TextField);
813 switch (event->type) {
814 case FocusIn:
815 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
816 return;
817 tPtr->flags.focused = 1;
818 #if 0
819 if (!tPtr->timerID) {
820 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
821 blinkCursor, tPtr);
823 #endif
824 paintTextField(tPtr);
826 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
828 tPtr->flags.notIllegalMovement = 0;
829 break;
831 case FocusOut:
832 tPtr->flags.focused = 0;
833 #if 0
834 if (tPtr->timerID)
835 WMDeleteTimerHandler(tPtr->timerID);
836 tPtr->timerID = NULL;
837 #endif
839 paintTextField(tPtr);
840 if (!tPtr->flags.notIllegalMovement) {
841 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
842 (void*)WMIllegalTextMovement);
844 break;
846 case Expose:
847 if (event->xexpose.count!=0)
848 break;
849 paintTextField(tPtr);
850 break;
852 case DestroyNotify:
853 destroyTextField(tPtr);
854 break;
859 static void
860 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
862 char buffer[64];
863 KeySym ksym;
864 int count, refresh = 0;
865 int control_pressed = 0;
867 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
868 control_pressed = 1;
871 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
872 buffer[count] = '\0';
874 if (!(event->xkey.state & ShiftMask)) {
875 if (tPtr->selection.count)
876 refresh = 1;
877 tPtr->prevselection = tPtr->selection;
878 tPtr->selection.position = tPtr->cursorPosition;
879 tPtr->selection.count = 0;
882 /* Be careful in any case in this switch statement, never to call
883 * to more than a function that can generate text change notifications.
884 * Only one text change notification should be sent in any case.
885 * Else hazardous things can happen.
886 * Maybe we need a better solution than the function wrapper to inform
887 * functions that change text in text fields, if they need to send a
888 * change notification or not. -Dan
890 switch (ksym) {
891 case XK_Tab:
892 case XK_ISO_Left_Tab:
893 if (event->xkey.state & ShiftMask) {
894 if (tPtr->view->prevFocusChain) {
895 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
896 tPtr->view->prevFocusChain);
897 tPtr->flags.notIllegalMovement = 1;
899 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
900 (void*)WMBacktabTextMovement);
901 } else {
902 if (tPtr->view->nextFocusChain) {
903 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
904 tPtr->view->nextFocusChain);
905 tPtr->flags.notIllegalMovement = 1;
907 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
908 (void*)WMTabTextMovement);
910 break;
912 case XK_Return:
913 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
914 (void*)WMReturnTextMovement);
915 break;
917 case WM_EMACSKEY_LEFT:
918 if (!control_pressed) {
919 goto normal_key;
921 case XK_KP_Left:
922 case XK_Left:
923 if (tPtr->cursorPosition > 0) {
924 paintCursor(tPtr);
925 if (event->xkey.state & ControlMask) {
926 int i;
927 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
928 if (tPtr->text[i] == ' ' || i == 0) {
929 tPtr->cursorPosition = i;
930 break;
932 } else {
933 tPtr->cursorPosition--;
935 if (tPtr->cursorPosition < tPtr->viewPosition) {
936 tPtr->viewPosition = tPtr->cursorPosition;
937 refresh = 1;
938 } else {
939 paintCursor(tPtr);
942 break;
944 case WM_EMACSKEY_RIGHT:
945 if (!control_pressed) {
946 goto normal_key;
948 case XK_KP_Right:
949 case XK_Right:
950 if (tPtr->cursorPosition < tPtr->textLen) {
951 paintCursor(tPtr);
952 if (event->xkey.state & ControlMask) {
953 int i;
954 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
955 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
956 tPtr->cursorPosition = i;
957 break;
959 } else {
960 tPtr->cursorPosition++;
962 while (WMWidthOfString(tPtr->font,
963 &(tPtr->text[tPtr->viewPosition]),
964 tPtr->cursorPosition-tPtr->viewPosition)
965 > tPtr->usableWidth) {
966 tPtr->viewPosition++;
967 refresh = 1;
969 if (!refresh)
970 paintCursor(tPtr);
972 break;
974 case WM_EMACSKEY_HOME:
975 if (!control_pressed) {
976 goto normal_key;
978 case XK_KP_Home:
979 case XK_Home:
980 if (tPtr->cursorPosition > 0) {
981 paintCursor(tPtr);
982 tPtr->cursorPosition = 0;
983 if (tPtr->viewPosition > 0) {
984 tPtr->viewPosition = 0;
985 refresh = 1;
986 } else {
987 paintCursor(tPtr);
990 break;
992 case WM_EMACSKEY_END:
993 if (!control_pressed) {
994 goto normal_key;
996 case XK_KP_End:
997 case XK_End:
998 if (tPtr->cursorPosition < tPtr->textLen) {
999 paintCursor(tPtr);
1000 tPtr->cursorPosition = tPtr->textLen;
1001 tPtr->viewPosition = 0;
1002 while (WMWidthOfString(tPtr->font,
1003 &(tPtr->text[tPtr->viewPosition]),
1004 tPtr->textLen-tPtr->viewPosition)
1005 > tPtr->usableWidth) {
1006 tPtr->viewPosition++;
1007 refresh = 1;
1009 if (!refresh)
1010 paintCursor(tPtr);
1012 break;
1014 case WM_EMACSKEY_BS:
1015 if (!control_pressed) {
1016 goto normal_key;
1018 case XK_BackSpace:
1019 if (tPtr->cursorPosition > 0) {
1020 WMRange range;
1022 if (tPtr->prevselection.count) {
1023 range.position = tPtr->prevselection.count < 0
1024 ? tPtr->prevselection.position + tPtr->prevselection.count
1025 : tPtr->prevselection.position;
1027 range.count = abs(tPtr->prevselection.count);
1028 } else {
1029 range.position = tPtr->cursorPosition - 1;
1030 range.count = 1;
1032 WMDeleteTextFieldRange(tPtr, range);
1034 break;
1036 case WM_EMACSKEY_DEL:
1037 if (!control_pressed) {
1038 goto normal_key;
1040 case XK_KP_Delete:
1041 case XK_Delete:
1042 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1043 WMRange range;
1045 if (tPtr->prevselection.count) {
1046 range.position = tPtr->prevselection.count < 0
1047 ? tPtr->prevselection.position + tPtr->prevselection.count
1048 : tPtr->prevselection.position;
1050 range.count = abs(tPtr->prevselection.count);
1051 } else {
1052 range.position = tPtr->cursorPosition;
1053 range.count = 1;
1055 WMDeleteTextFieldRange(tPtr, range);
1057 break;
1059 normal_key:
1060 default:
1061 if (count > 0 && !iscntrl(buffer[0])) {
1062 WMRange range;
1064 if (tPtr->prevselection.count) {
1065 range.position = tPtr->prevselection.count < 0
1066 ? tPtr->prevselection.position + tPtr->prevselection.count
1067 : tPtr->prevselection.position;
1069 range.count = abs(tPtr->prevselection.count);
1070 } else {
1071 range.position = tPtr->cursorPosition;
1072 range.count = 1;
1074 if (tPtr->prevselection.count)
1075 deleteTextFieldRange(tPtr, range);
1076 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1077 } else {
1078 return;
1080 break;
1082 if (event->xkey.state & ShiftMask) {
1083 if (tPtr->selection.count == 0)
1084 tPtr->selection.position = tPtr->cursorPosition;
1085 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1086 refresh = 1;
1088 tPtr->prevselection.count = 0;
1089 if (refresh) {
1090 paintTextField(tPtr);
1095 static int
1096 pointToCursorPosition(TextField *tPtr, int x)
1098 int a, b, mid;
1099 int tw;
1101 if (tPtr->flags.bordered)
1102 x -= 2;
1104 a = tPtr->viewPosition;
1105 b = tPtr->viewPosition + tPtr->textLen;
1106 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1107 tPtr->textLen-tPtr->viewPosition) < x)
1108 return tPtr->textLen;
1110 while (a < b && b-a>1) {
1111 mid = (a+b)/2;
1112 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1113 mid - tPtr->viewPosition);
1114 if (tw > x)
1115 b = mid;
1116 else if (tw < x)
1117 a = mid;
1118 else
1119 return mid;
1121 return (a+b)/2;
1125 static void
1126 handleTextFieldActionEvents(XEvent *event, void *data)
1128 TextField *tPtr = (TextField*)data;
1129 static int move;
1131 CHECK_CLASS(data, WC_TextField);
1133 switch (event->type) {
1134 case KeyPress:
1135 if (tPtr->flags.enabled && tPtr->flags.focused) {
1136 handleTextFieldKeyPress(tPtr, event);
1137 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1138 W_VIEW(tPtr)->window, False,
1139 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1140 GrabModeAsync, GrabModeAsync, None,
1141 W_VIEW(tPtr)->screen->invisibleCursor,
1142 CurrentTime);
1143 tPtr->flags.pointerGrabbed = 1;
1145 break;
1147 case MotionNotify:
1149 if (tPtr->flags.pointerGrabbed) {
1150 tPtr->flags.pointerGrabbed = 0;
1151 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1154 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1156 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1157 tPtr->usableWidth) {
1158 if (WMWidthOfString(tPtr->font,
1159 &(tPtr->text[tPtr->viewPosition]),
1160 tPtr->cursorPosition-tPtr->viewPosition)
1161 > tPtr->usableWidth) {
1162 tPtr->viewPosition++;
1164 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1165 paintCursor(tPtr);
1166 tPtr->viewPosition--;
1169 if (!tPtr->selection.count) {
1170 tPtr->selection.position = tPtr->cursorPosition;
1173 tPtr->cursorPosition =
1174 pointToCursorPosition(tPtr, event->xmotion.x);
1176 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1179 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1182 paintCursor(tPtr);
1183 paintTextField(tPtr);
1186 if (move) {
1187 int count;
1188 XSetSelectionOwner(tPtr->view->screen->display,
1189 XA_PRIMARY, None, CurrentTime);
1190 count = tPtr->selection.count < 0
1191 ? tPtr->selection.position + tPtr->selection.count
1192 : tPtr->selection.position;
1193 XStoreBuffer(tPtr->view->screen->display,
1194 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1196 break;
1198 case ButtonPress:
1199 if (tPtr->flags.pointerGrabbed) {
1200 tPtr->flags.pointerGrabbed = 0;
1201 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1202 break;
1205 move = 1;
1206 switch (tPtr->flags.alignment) {
1207 int textWidth;
1208 case WARight:
1209 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1210 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1211 WMSetFocusToWidget(tPtr);
1212 } else if (tPtr->flags.focused) {
1213 tPtr->selection.count = 0;
1215 if(textWidth < tPtr->usableWidth){
1216 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1217 event->xbutton.x - tPtr->usableWidth
1218 + textWidth);
1220 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1221 event->xbutton.x);
1223 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1224 event->xbutton.x);
1225 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1228 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1229 event->xbutton.x);
1231 paintTextField(tPtr);
1232 break;
1234 case WALeft:
1235 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1236 WMSetFocusToWidget(tPtr);
1237 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1238 event->xbutton.x);
1239 paintTextField(tPtr);
1240 } else if (tPtr->flags.focused) {
1241 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1242 event->xbutton.x);
1243 tPtr->selection.count = 0;
1244 paintTextField(tPtr);
1246 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1247 char *text;
1249 text = W_GetTextSelection(tPtr->view->screen,
1250 tPtr->view->screen->clipboardAtom);
1251 if (!text) {
1252 text = W_GetTextSelection(tPtr->view->screen,
1253 XA_CUT_BUFFER0);
1255 if (text) {
1256 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1257 XFree(text);
1258 NOTIFY(tPtr, didChange, WMTextDidChangeNotification, NULL);
1261 break;
1263 break;
1265 case ButtonRelease:
1266 if (tPtr->flags.pointerGrabbed) {
1267 tPtr->flags.pointerGrabbed = 0;
1268 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1271 move = 0;
1272 break;
1277 static void
1278 destroyTextField(TextField *tPtr)
1280 #if 0
1281 if (tPtr->timerID)
1282 WMDeleteTimerHandler(tPtr->timerID);
1283 #endif
1285 WMReleaseFont(tPtr->font);
1287 if (tPtr->text)
1288 free(tPtr->text);
1290 free(tPtr);