Simplified code for XK_ISO_Left_Tab
[wmaker-crm.git] / WINGs / wtextfield.c
blob020b162a6784b9771dc87dde83e9c6f9aa3bc83f
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 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
17 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
18 char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
21 typedef struct W_TextField {
22 W_Class widgetClass;
23 W_View *view;
25 #if 0
26 struct W_TextField *nextField; /* next textfield in the chain */
27 struct W_TextField *prevField;
28 #endif
30 char *text;
31 int textLen; /* size of text */
32 int bufferSize; /* memory allocated for text */
34 int viewPosition; /* position of text being shown */
36 int cursorPosition; /* position of the insertion cursor */
38 short usableWidth;
39 short offsetWidth; /* offset of text from border */
41 WMRange selection;
42 WMRange prevselection;
44 WMFont *font;
46 #if 0
47 WMHandlerID timerID; /* for cursor blinking */
48 #endif
49 struct {
50 WMAlignment alignment:2;
52 unsigned int bordered:1;
54 unsigned int beveled:1;
56 unsigned int enabled:1;
58 unsigned int focused:1;
60 unsigned int cursorOn:1;
62 unsigned int secure:1; /* password entry style */
64 unsigned int pointerGrabbed:1;
66 /**/
67 unsigned int notIllegalMovement:1;
68 } flags;
69 } TextField;
72 #define MIN_TEXT_BUFFER 2
73 #define TEXT_BUFFER_INCR 8
76 #define WM_EMACSKEYMASK ControlMask
78 #define WM_EMACSKEY_LEFT XK_b
79 #define WM_EMACSKEY_RIGHT XK_f
80 #define WM_EMACSKEY_HOME XK_a
81 #define WM_EMACSKEY_END XK_e
82 #define WM_EMACSKEY_BS XK_h
83 #define WM_EMACSKEY_DEL XK_d
87 #define DEFAULT_WIDTH 60
88 #define DEFAULT_HEIGHT 20
89 #define DEFAULT_BORDERED True
90 #define DEFAULT_ALIGNMENT WALeft
94 static void destroyTextField(TextField *tPtr);
95 static void paintTextField(TextField *tPtr);
97 static void handleEvents(XEvent *event, void *data);
98 static void handleTextFieldActionEvents(XEvent *event, void *data);
99 static void resizeTextField();
101 struct W_ViewProcedureTable _TextFieldViewProcedures = {
102 NULL,
103 resizeTextField,
104 NULL
108 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
109 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
111 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
112 &((tPtr)->text[(start)]), (end) - (start) + 1))
115 static void
116 memmv(char *dest, char *src, int size)
118 int i;
120 if (dest > src) {
121 for (i=size-1; i>=0; i--) {
122 dest[i] = src[i];
124 } else if (dest < src) {
125 for (i=0; i<size; i++) {
126 dest[i] = src[i];
132 static int
133 incrToFit(TextField *tPtr)
135 int vp = tPtr->viewPosition;
137 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
138 tPtr->viewPosition++;
140 return vp!=tPtr->viewPosition;
144 static int
145 incrToFit2(TextField *tPtr)
147 int vp = tPtr->viewPosition;
148 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
149 >= tPtr->usableWidth)
150 tPtr->viewPosition++;
153 return vp!=tPtr->viewPosition;
157 static void
158 decrToFit(TextField *tPtr)
160 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
161 && tPtr->viewPosition>0)
162 tPtr->viewPosition--;
165 #undef TEXT_WIDTH
166 #undef TEXT_WIDTH2
169 WMTextField*
170 WMCreateTextField(WMWidget *parent)
172 TextField *tPtr;
174 tPtr = wmalloc(sizeof(TextField));
175 memset(tPtr, 0, sizeof(TextField));
177 tPtr->widgetClass = WC_TextField;
179 tPtr->view = W_CreateView(W_VIEW(parent));
180 if (!tPtr->view) {
181 free(tPtr);
182 return NULL;
184 tPtr->view->self = tPtr;
186 tPtr->view->attribFlags |= CWCursor;
187 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
189 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
191 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
192 tPtr->text[0] = 0;
193 tPtr->textLen = 0;
194 tPtr->bufferSize = MIN_TEXT_BUFFER;
196 tPtr->flags.enabled = 1;
198 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
199 |FocusChangeMask, handleEvents, tPtr);
201 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
203 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
205 tPtr->flags.bordered = DEFAULT_BORDERED;
206 tPtr->flags.beveled = True;
207 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
208 tPtr->offsetWidth =
209 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
211 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
212 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
213 handleTextFieldActionEvents, tPtr);
215 tPtr->flags.cursorOn = 1;
217 return tPtr;
221 void
222 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
224 int len;
226 CHECK_CLASS(tPtr, WC_TextField);
228 if (!text)
229 return;
231 len = strlen(text);
233 /* check if buffer will hold the text */
234 if (len + tPtr->textLen >= tPtr->bufferSize) {
235 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
236 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
239 if (position < 0 || position >= tPtr->textLen) {
240 /* append the text at the end */
241 strcat(tPtr->text, text);
243 incrToFit(tPtr);
245 tPtr->textLen += len;
246 tPtr->cursorPosition += len;
247 } else {
248 /* insert text at position */
249 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
250 tPtr->textLen-position+1);
252 memcpy(&(tPtr->text[position]), text, len);
254 tPtr->textLen += len;
255 if (position >= tPtr->cursorPosition) {
256 tPtr->cursorPosition += len;
257 incrToFit2(tPtr);
258 } else {
259 incrToFit(tPtr);
263 paintTextField(tPtr);
265 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
266 (void*)WMInsertTextEvent);
270 static void
271 deleteTextFieldRange(WMTextField *tPtr, WMRange range)
273 CHECK_CLASS(tPtr, WC_TextField);
275 if (range.position >= tPtr->textLen)
276 return;
278 if (range.count < 1) {
279 if (range.position < 0)
280 range.position = 0;
281 tPtr->text[range.position] = 0;
282 tPtr->textLen = range.position;
284 tPtr->cursorPosition = 0;
285 tPtr->viewPosition = 0;
286 } else {
287 if (range.position + range.count > tPtr->textLen)
288 range.count = tPtr->textLen - range.position;
289 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
290 tPtr->textLen - (range.position+range.count) + 1);
291 tPtr->textLen -= range.count;
293 if (tPtr->cursorPosition > range.position)
294 tPtr->cursorPosition -= range.count;
296 decrToFit(tPtr);
299 paintTextField(tPtr);
303 void
304 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
306 deleteTextFieldRange(tPtr, range);
307 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
308 (void*)WMDeleteTextEvent);
313 char*
314 WMGetTextFieldText(WMTextField *tPtr)
316 CHECK_CLASS(tPtr, WC_TextField);
318 return wstrdup(tPtr->text);
322 void
323 WMSetTextFieldText(WMTextField *tPtr, char *text)
325 if ((text && strcmp(tPtr->text, text) == 0) ||
326 (!text && tPtr->textLen == 0))
327 return;
329 if (text==NULL) {
330 tPtr->text[0] = 0;
331 tPtr->textLen = 0;
332 } else {
333 tPtr->textLen = strlen(text);
335 if (tPtr->textLen >= tPtr->bufferSize) {
336 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
337 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
339 strcpy(tPtr->text, text);
342 if (tPtr->textLen < tPtr->cursorPosition)
343 tPtr->cursorPosition = tPtr->textLen;
345 tPtr->cursorPosition = tPtr->textLen;
346 tPtr->viewPosition = 0;
347 tPtr->selection.count = 0;
349 if (tPtr->view->flags.realized)
350 paintTextField(tPtr);
352 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
353 (void*)WMSetTextEvent);
357 void
358 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
360 /* TODO: update font change after field is mapped */
361 WMReleaseFont(tPtr->font);
362 tPtr->font = WMRetainFont(font);
365 void
366 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
368 tPtr->flags.alignment = alignment;
369 if (alignment!=WALeft) {
370 wwarning("only left alignment is supported in textfields");
371 return;
374 if (tPtr->view->flags.realized) {
375 paintTextField(tPtr);
380 void
381 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
383 tPtr->flags.bordered = bordered;
385 if (tPtr->view->flags.realized) {
386 paintTextField(tPtr);
391 void
392 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
394 tPtr->flags.beveled = flag;
396 if (tPtr->view->flags.realized) {
397 paintTextField(tPtr);
403 void
404 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
406 tPtr->flags.secure = flag;
408 if (tPtr->view->flags.realized) {
409 paintTextField(tPtr);
414 Bool
415 WMGetTextFieldEditable(WMTextField *tPtr)
417 return tPtr->flags.enabled;
421 void
422 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
424 tPtr->flags.enabled = flag;
426 if (tPtr->view->flags.realized) {
427 paintTextField(tPtr);
432 void
433 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
435 if (tPtr->flags.enabled) {
436 if (range.position < 0) {
437 range.count += range.position;
438 range.count = (range.count < 0) ? 0 : range.count;
439 range.position = 0;
440 } else if (range.position > tPtr->textLen) {
441 range.position = tPtr->textLen;
442 range.count = 0;
445 if (range.position + range.count > tPtr->textLen)
446 range.count = tPtr->textLen - range.position;
448 tPtr->prevselection = tPtr->selection; /* check if this is needed */
450 tPtr->selection = range;
452 if (tPtr->view->flags.realized) {
453 paintTextField(tPtr);
459 void
460 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
462 if (tPtr->flags.enabled) {
463 if (position > tPtr->textLen)
464 position = tPtr->textLen;
466 tPtr->cursorPosition = position;
467 if (tPtr->view->flags.realized) {
468 paintTextField(tPtr);
474 void
475 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
477 CHECK_CLASS(tPtr, WC_TextField);
478 if (next == NULL) {
479 if (tPtr->view->nextFocusChain)
480 tPtr->view->nextFocusChain->prevFocusChain = NULL;
481 tPtr->view->nextFocusChain = NULL;
482 return;
485 CHECK_CLASS(next, WC_TextField);
487 if (tPtr->view->nextFocusChain)
488 tPtr->view->nextFocusChain->prevFocusChain = NULL;
489 if (next->view->prevFocusChain)
490 next->view->prevFocusChain->nextFocusChain = NULL;
492 tPtr->view->nextFocusChain = next->view;
493 next->view->prevFocusChain = tPtr->view;
497 void
498 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
500 CHECK_CLASS(tPtr, WC_TextField);
501 if (prev == NULL) {
502 if (tPtr->view->prevFocusChain)
503 tPtr->view->prevFocusChain->nextFocusChain = NULL;
504 tPtr->view->prevFocusChain = NULL;
505 return;
508 CHECK_CLASS(prev, WC_TextField);
510 if (tPtr->view->prevFocusChain)
511 tPtr->view->prevFocusChain->nextFocusChain = NULL;
512 if (prev->view->nextFocusChain)
513 prev->view->nextFocusChain->prevFocusChain = NULL;
515 tPtr->view->prevFocusChain = prev->view;
516 prev->view->nextFocusChain = tPtr->view;
520 static void
521 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
523 W_ResizeView(tPtr->view, width, height);
525 tPtr->offsetWidth =
526 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
528 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
532 static char*
533 makeHiddenString(int length)
535 char *data = wmalloc(length+1);
537 memset(data, '*', length);
538 data[length] = '\0';
539 return data;
543 static void
544 paintCursor(TextField *tPtr)
546 int cx;
547 WMScreen *screen = tPtr->view->screen;
548 int textWidth;
549 char *text;
551 if (tPtr->flags.secure)
552 text = makeHiddenString(strlen(tPtr->text));
553 else
554 text = tPtr->text;
556 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
557 tPtr->cursorPosition-tPtr->viewPosition);
559 switch (tPtr->flags.alignment) {
560 case WARight:
561 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
562 if (textWidth < tPtr->usableWidth)
563 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
564 else
565 cx += tPtr->offsetWidth + 1;
566 break;
567 case WALeft:
568 cx += tPtr->offsetWidth + 1;
569 break;
570 case WAJustified:
571 /* not supported */
572 case WACenter:
573 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
574 if (textWidth < tPtr->usableWidth)
575 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
576 else
577 cx += tPtr->offsetWidth;
578 break;
581 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
582 cx, tPtr->offsetWidth, 1,
583 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
584 printf("%d %d\n",cx,tPtr->cursorPosition);
586 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
587 cx, tPtr->offsetWidth, cx,
588 tPtr->view->size.height - tPtr->offsetWidth - 1);
590 if (tPtr->flags.secure)
591 free(text);
596 static void
597 drawRelief(WMView *view, Bool beveled)
599 WMScreen *scr = view->screen;
600 Display *dpy = scr->display;
601 GC wgc;
602 GC lgc;
603 GC dgc;
604 int width = view->size.width;
605 int height = view->size.height;
607 dgc = WMColorGC(scr->darkGray);
609 if (!beveled) {
610 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
612 return;
614 wgc = WMColorGC(scr->white);
615 lgc = WMColorGC(scr->gray);
617 /* top left */
618 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
619 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
621 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
622 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
624 /* bottom right */
625 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
626 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
628 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
629 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
633 static void
634 paintTextField(TextField *tPtr)
636 W_Screen *screen = tPtr->view->screen;
637 W_View *view = tPtr->view;
638 W_View viewbuffer;
639 int tx, ty, tw, th;
640 int rx;
641 int bd;
642 int totalWidth;
643 char *text;
644 Pixmap drawbuffer;
647 if (!view->flags.realized || !view->flags.mapped)
648 return;
650 if (!tPtr->flags.bordered) {
651 bd = 0;
652 } else {
653 bd = 2;
656 if (tPtr->flags.secure) {
657 text = makeHiddenString(strlen(tPtr->text));
658 } else {
659 text = tPtr->text;
662 totalWidth = tPtr->view->size.width - 2*bd;
664 drawbuffer = XCreatePixmap(screen->display, view->window,
665 view->size.width, view->size.height, screen->depth);
666 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
667 0,0, view->size.width,view->size.height);
668 /* this is quite dirty */
669 viewbuffer.screen = view->screen;
670 viewbuffer.size = view->size;
671 viewbuffer.window = drawbuffer;
674 if (tPtr->textLen > 0) {
675 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
676 tPtr->textLen - tPtr->viewPosition);
678 th = WMFontHeight(tPtr->font);
680 ty = tPtr->offsetWidth;
681 switch (tPtr->flags.alignment) {
682 case WALeft:
683 tx = tPtr->offsetWidth + 1;
684 if (tw < tPtr->usableWidth)
685 XFillRectangle(screen->display, drawbuffer,
686 WMColorGC(screen->white),
687 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
688 break;
690 case WACenter:
691 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
692 if (tw < tPtr->usableWidth)
693 XClearArea(screen->display, view->window, bd, bd,
694 totalWidth, view->size.height-2*bd, False);
695 break;
697 default:
698 case WARight:
699 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
700 if (tw < tPtr->usableWidth)
701 XClearArea(screen->display, view->window, bd, bd,
702 totalWidth-tw, view->size.height-2*bd, False);
703 break;
706 if (!tPtr->flags.enabled)
707 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
709 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
710 tPtr->font, tx, ty,
711 &(text[tPtr->viewPosition]),
712 tPtr->textLen - tPtr->viewPosition);
714 if (tPtr->selection.count) {
715 int count,count2;
717 count = tPtr->selection.count < 0
718 ? tPtr->selection.position + tPtr->selection.count
719 : tPtr->selection.position;
720 count2 = abs(tPtr->selection.count);
721 if (count < tPtr->viewPosition) {
722 count2 = abs(count2 - abs(tPtr->viewPosition - count));
723 count = tPtr->viewPosition;
727 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
728 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
730 XSetBackground(screen->display, screen->textFieldGC,
731 screen->gray->color.pixel);
733 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
734 tPtr->font, rx, ty, &(text[count]),
735 count2);
737 XSetBackground(screen->display, screen->textFieldGC,
738 screen->white->color.pixel);
741 if (!tPtr->flags.enabled)
742 WMSetColorInGC(screen->black, screen->textFieldGC);
743 } else {
744 XFillRectangle(screen->display, drawbuffer,
745 WMColorGC(screen->white),
746 bd,bd, totalWidth,view->size.height-2*bd);
749 /* draw relief */
750 if (tPtr->flags.bordered) {
751 drawRelief(&viewbuffer, tPtr->flags.beveled);
754 if (tPtr->flags.secure)
755 free(text);
756 XCopyArea(screen->display, drawbuffer, view->window,
757 screen->copyGC, 0,0, view->size.width,
758 view->size.height,0,0);
759 XFreePixmap(screen->display, drawbuffer);
761 /* draw cursor */
762 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
763 paintCursor(tPtr);
768 #if 0
769 static void
770 blinkCursor(void *data)
772 TextField *tPtr = (TextField*)data;
774 if (tPtr->flags.cursorOn) {
775 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
776 data);
777 } else {
778 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
779 data);
781 paintCursor(tPtr);
782 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
784 #endif
787 static void
788 handleEvents(XEvent *event, void *data)
790 TextField *tPtr = (TextField*)data;
792 CHECK_CLASS(data, WC_TextField);
795 switch (event->type) {
796 case FocusIn:
797 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
798 return;
799 tPtr->flags.focused = 1;
800 #if 0
801 if (!tPtr->timerID) {
802 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
803 blinkCursor, tPtr);
805 #endif
806 paintTextField(tPtr);
808 WMPostNotificationName(WMTextDidBeginEditingNotification, tPtr, NULL);
810 tPtr->flags.notIllegalMovement = 0;
811 break;
813 case FocusOut:
814 tPtr->flags.focused = 0;
815 #if 0
816 if (tPtr->timerID)
817 WMDeleteTimerHandler(tPtr->timerID);
818 tPtr->timerID = NULL;
819 #endif
821 paintTextField(tPtr);
822 if (!tPtr->flags.notIllegalMovement) {
823 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
824 (void*)WMIllegalTextMovement);
826 break;
828 case Expose:
829 if (event->xexpose.count!=0)
830 break;
831 paintTextField(tPtr);
832 break;
834 case DestroyNotify:
835 destroyTextField(tPtr);
836 break;
841 static void
842 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
844 char buffer[64];
845 KeySym ksym;
846 int count, refresh = 0;
847 int control_pressed = 0;
849 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
850 control_pressed = 1;
853 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
854 buffer[count] = '\0';
856 if (!(event->xkey.state & ShiftMask)) {
857 if (tPtr->selection.count)
858 refresh = 1;
859 tPtr->prevselection = tPtr->selection;
860 tPtr->selection.position = tPtr->cursorPosition;
861 tPtr->selection.count = 0;
864 /* Be careful in any case in this switch statement, never to call
865 * to more than a function that can generate text change notifications.
866 * Only one text change notification should be sent in any case.
867 * Else hazardous things can happen.
868 * Maybe we need a better solution than the function wrapper to inform
869 * functions that change text in text fields, if they need to send a
870 * change notification or not. -Dan
872 switch (ksym) {
873 case XK_Tab:
874 case XK_ISO_Left_Tab:
875 if (event->xkey.state & ShiftMask) {
876 if (tPtr->view->prevFocusChain) {
877 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
878 tPtr->view->prevFocusChain);
879 tPtr->flags.notIllegalMovement = 1;
881 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
882 (void*)WMBacktabTextMovement);
883 } else {
884 if (tPtr->view->nextFocusChain) {
885 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
886 tPtr->view->nextFocusChain);
887 tPtr->flags.notIllegalMovement = 1;
889 WMPostNotificationName(WMTextDidEndEditingNotification,
890 tPtr, (void*)WMTabTextMovement);
892 break;
894 case XK_Return:
895 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
896 (void*)WMReturnTextMovement);
897 break;
899 case WM_EMACSKEY_LEFT:
900 if (!control_pressed) {
901 goto normal_key;
903 case XK_KP_Left:
904 case XK_Left:
905 if (tPtr->cursorPosition > 0) {
906 paintCursor(tPtr);
907 if (event->xkey.state & ControlMask) {
908 int i;
909 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
910 if (tPtr->text[i] == ' ' || i == 0) {
911 tPtr->cursorPosition = i;
912 break;
914 } else {
915 tPtr->cursorPosition--;
917 if (tPtr->cursorPosition < tPtr->viewPosition) {
918 tPtr->viewPosition = tPtr->cursorPosition;
919 refresh = 1;
920 } else {
921 paintCursor(tPtr);
924 break;
926 case WM_EMACSKEY_RIGHT:
927 if (!control_pressed) {
928 goto normal_key;
930 case XK_KP_Right:
931 case XK_Right:
932 if (tPtr->cursorPosition < tPtr->textLen) {
933 paintCursor(tPtr);
934 if (event->xkey.state & ControlMask) {
935 int i;
936 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
937 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
938 tPtr->cursorPosition = i;
939 break;
941 } else {
942 tPtr->cursorPosition++;
944 while (WMWidthOfString(tPtr->font,
945 &(tPtr->text[tPtr->viewPosition]),
946 tPtr->cursorPosition-tPtr->viewPosition)
947 > tPtr->usableWidth) {
948 tPtr->viewPosition++;
949 refresh = 1;
951 if (!refresh)
952 paintCursor(tPtr);
954 break;
956 case WM_EMACSKEY_HOME:
957 if (!control_pressed) {
958 goto normal_key;
960 case XK_KP_Home:
961 case XK_Home:
962 if (tPtr->cursorPosition > 0) {
963 paintCursor(tPtr);
964 tPtr->cursorPosition = 0;
965 if (tPtr->viewPosition > 0) {
966 tPtr->viewPosition = 0;
967 refresh = 1;
968 } else {
969 paintCursor(tPtr);
972 break;
974 case WM_EMACSKEY_END:
975 if (!control_pressed) {
976 goto normal_key;
978 case XK_KP_End:
979 case XK_End:
980 if (tPtr->cursorPosition < tPtr->textLen) {
981 paintCursor(tPtr);
982 tPtr->cursorPosition = tPtr->textLen;
983 tPtr->viewPosition = 0;
984 while (WMWidthOfString(tPtr->font,
985 &(tPtr->text[tPtr->viewPosition]),
986 tPtr->textLen-tPtr->viewPosition)
987 > tPtr->usableWidth) {
988 tPtr->viewPosition++;
989 refresh = 1;
991 if (!refresh)
992 paintCursor(tPtr);
994 break;
996 case WM_EMACSKEY_BS:
997 if (!control_pressed) {
998 goto normal_key;
1000 case XK_BackSpace:
1001 if (tPtr->cursorPosition > 0) {
1002 WMRange range;
1004 if (tPtr->prevselection.count) {
1005 range.position = tPtr->prevselection.count < 0
1006 ? tPtr->prevselection.position + tPtr->prevselection.count
1007 : tPtr->prevselection.position;
1009 range.count = abs(tPtr->prevselection.count);
1010 } else {
1011 range.position = tPtr->cursorPosition - 1;
1012 range.count = 1;
1014 WMDeleteTextFieldRange(tPtr, range);
1016 break;
1018 case WM_EMACSKEY_DEL:
1019 if (!control_pressed) {
1020 goto normal_key;
1022 case XK_KP_Delete:
1023 case XK_Delete:
1024 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1025 WMRange range;
1027 if (tPtr->prevselection.count) {
1028 range.position = tPtr->prevselection.count < 0
1029 ? tPtr->prevselection.position + tPtr->prevselection.count
1030 : tPtr->prevselection.position;
1032 range.count = abs(tPtr->prevselection.count);
1033 } else {
1034 range.position = tPtr->cursorPosition;
1035 range.count = 1;
1037 WMDeleteTextFieldRange(tPtr, range);
1039 break;
1041 normal_key:
1042 default:
1043 if (count > 0 && !iscntrl(buffer[0])) {
1044 WMRange range;
1046 if (tPtr->prevselection.count) {
1047 range.position = tPtr->prevselection.count < 0
1048 ? tPtr->prevselection.position + tPtr->prevselection.count
1049 : tPtr->prevselection.position;
1051 range.count = abs(tPtr->prevselection.count);
1052 } else {
1053 range.position = tPtr->cursorPosition;
1054 range.count = 1;
1056 if (tPtr->prevselection.count)
1057 deleteTextFieldRange(tPtr, range);
1058 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1059 } else {
1060 return;
1062 break;
1064 if (event->xkey.state & ShiftMask) {
1065 if (tPtr->selection.count == 0)
1066 tPtr->selection.position = tPtr->cursorPosition;
1067 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1068 refresh = 1;
1070 tPtr->prevselection.count = 0;
1071 if (refresh) {
1072 paintTextField(tPtr);
1077 static int
1078 pointToCursorPosition(TextField *tPtr, int x)
1080 int a, b, mid;
1081 int tw;
1083 if (tPtr->flags.bordered)
1084 x -= 2;
1086 a = tPtr->viewPosition;
1087 b = tPtr->viewPosition + tPtr->textLen;
1088 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1089 tPtr->textLen-tPtr->viewPosition) < x)
1090 return tPtr->textLen;
1092 while (a < b && b-a>1) {
1093 mid = (a+b)/2;
1094 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1095 mid - tPtr->viewPosition);
1096 if (tw > x)
1097 b = mid;
1098 else if (tw < x)
1099 a = mid;
1100 else
1101 return mid;
1103 return (a+b)/2;
1107 static void
1108 handleTextFieldActionEvents(XEvent *event, void *data)
1110 TextField *tPtr = (TextField*)data;
1111 static int move;
1113 CHECK_CLASS(data, WC_TextField);
1115 switch (event->type) {
1116 case KeyPress:
1117 if (tPtr->flags.enabled && tPtr->flags.focused) {
1118 handleTextFieldKeyPress(tPtr, event);
1119 XGrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen),
1120 W_VIEW(tPtr)->window, False,
1121 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
1122 GrabModeAsync, GrabModeAsync, None,
1123 W_VIEW(tPtr)->screen->invisibleCursor,
1124 CurrentTime);
1125 tPtr->flags.pointerGrabbed = 1;
1127 break;
1129 case MotionNotify:
1131 if (tPtr->flags.pointerGrabbed) {
1132 tPtr->flags.pointerGrabbed = 0;
1133 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1136 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1138 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1139 tPtr->usableWidth) {
1140 if (WMWidthOfString(tPtr->font,
1141 &(tPtr->text[tPtr->viewPosition]),
1142 tPtr->cursorPosition-tPtr->viewPosition)
1143 > tPtr->usableWidth) {
1144 tPtr->viewPosition++;
1146 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1147 paintCursor(tPtr);
1148 tPtr->viewPosition--;
1151 if (!tPtr->selection.count) {
1152 tPtr->selection.position = tPtr->cursorPosition;
1155 tPtr->cursorPosition =
1156 pointToCursorPosition(tPtr, event->xmotion.x);
1158 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1161 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1164 paintCursor(tPtr);
1165 paintTextField(tPtr);
1168 if (move) {
1169 int count;
1170 XSetSelectionOwner(tPtr->view->screen->display,
1171 XA_PRIMARY, None, CurrentTime);
1172 count = tPtr->selection.count < 0
1173 ? tPtr->selection.position + tPtr->selection.count
1174 : tPtr->selection.position;
1175 XStoreBuffer(tPtr->view->screen->display,
1176 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1178 break;
1180 case ButtonPress:
1181 if (tPtr->flags.pointerGrabbed) {
1182 tPtr->flags.pointerGrabbed = 0;
1183 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1184 break;
1187 move = 1;
1188 switch (tPtr->flags.alignment) {
1189 int textWidth;
1190 case WARight:
1191 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1192 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1193 WMSetFocusToWidget(tPtr);
1194 } else if (tPtr->flags.focused) {
1195 tPtr->selection.count = 0;
1197 if(textWidth < tPtr->usableWidth){
1198 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1199 event->xbutton.x - tPtr->usableWidth
1200 + textWidth);
1202 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1203 event->xbutton.x);
1205 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1206 event->xbutton.x);
1207 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1210 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1211 event->xbutton.x);
1213 paintTextField(tPtr);
1214 break;
1216 case WALeft:
1217 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1218 WMSetFocusToWidget(tPtr);
1219 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1220 event->xbutton.x);
1221 paintTextField(tPtr);
1222 } else if (tPtr->flags.focused) {
1223 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1224 event->xbutton.x);
1225 tPtr->selection.count = 0;
1226 paintTextField(tPtr);
1228 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1229 char *text;
1231 text = W_GetTextSelection(tPtr->view->screen,
1232 tPtr->view->screen->clipboardAtom);
1233 if (!text) {
1234 text = W_GetTextSelection(tPtr->view->screen, XA_CUT_BUFFER0);
1236 if (text) {
1237 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1238 XFree(text);
1239 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
1242 break;
1244 break;
1246 case ButtonRelease:
1247 if (tPtr->flags.pointerGrabbed) {
1248 tPtr->flags.pointerGrabbed = 0;
1249 XUngrabPointer(WMScreenDisplay(W_VIEW(tPtr)->screen), CurrentTime);
1252 move = 0;
1253 break;
1258 static void
1259 destroyTextField(TextField *tPtr)
1261 #if 0
1262 if (tPtr->timerID)
1263 WMDeleteTimerHandler(tPtr->timerID);
1264 #endif
1266 WMReleaseFont(tPtr->font);
1268 if (tPtr->text)
1269 free(tPtr->text);
1271 free(tPtr);