Try to reduce flicker in text widget.
[wmaker-crm.git] / WINGs / wtextfield.c
blobeb00b420593506415e08b41ea5bdaf1d8fb15385
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 /**/
65 unsigned int notIllegalMovement:1;
66 } flags;
67 } TextField;
70 #define MIN_TEXT_BUFFER 2
71 #define TEXT_BUFFER_INCR 8
74 #define WM_EMACSKEYMASK ControlMask
76 #define WM_EMACSKEY_LEFT XK_b
77 #define WM_EMACSKEY_RIGHT XK_f
78 #define WM_EMACSKEY_HOME XK_a
79 #define WM_EMACSKEY_END XK_e
80 #define WM_EMACSKEY_BS XK_h
81 #define WM_EMACSKEY_DEL XK_d
85 #define DEFAULT_WIDTH 60
86 #define DEFAULT_HEIGHT 20
87 #define DEFAULT_BORDERED True
88 #define DEFAULT_ALIGNMENT WALeft
92 static void destroyTextField(TextField *tPtr);
93 static void paintTextField(TextField *tPtr);
95 static void handleEvents(XEvent *event, void *data);
96 static void handleTextFieldActionEvents(XEvent *event, void *data);
97 static void resizeTextField();
99 struct W_ViewProcedureTable _TextFieldViewProcedures = {
100 NULL,
101 resizeTextField,
102 NULL
106 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
107 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
109 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
110 &((tPtr)->text[(start)]), (end) - (start) + 1))
113 static void
114 memmv(char *dest, char *src, int size)
116 int i;
118 if (dest > src) {
119 for (i=size-1; i>=0; i--) {
120 dest[i] = src[i];
122 } else if (dest < src) {
123 for (i=0; i<size; i++) {
124 dest[i] = src[i];
130 static int
131 incrToFit(TextField *tPtr)
133 int vp = tPtr->viewPosition;
135 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
136 tPtr->viewPosition++;
138 return vp!=tPtr->viewPosition;
142 static int
143 incrToFit2(TextField *tPtr)
145 int vp = tPtr->viewPosition;
146 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
147 >= tPtr->usableWidth)
148 tPtr->viewPosition++;
151 return vp!=tPtr->viewPosition;
155 static void
156 decrToFit(TextField *tPtr)
158 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
159 && tPtr->viewPosition>0)
160 tPtr->viewPosition--;
163 #undef TEXT_WIDTH
164 #undef TEXT_WIDTH2
167 WMTextField*
168 WMCreateTextField(WMWidget *parent)
170 TextField *tPtr;
172 tPtr = wmalloc(sizeof(TextField));
173 memset(tPtr, 0, sizeof(TextField));
175 tPtr->widgetClass = WC_TextField;
177 tPtr->view = W_CreateView(W_VIEW(parent));
178 if (!tPtr->view) {
179 free(tPtr);
180 return NULL;
182 tPtr->view->self = tPtr;
184 tPtr->view->attribFlags |= CWCursor;
185 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
187 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
189 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
190 tPtr->text[0] = 0;
191 tPtr->textLen = 0;
192 tPtr->bufferSize = MIN_TEXT_BUFFER;
194 tPtr->flags.enabled = 1;
196 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
197 |FocusChangeMask, handleEvents, tPtr);
199 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
201 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
203 tPtr->flags.bordered = DEFAULT_BORDERED;
204 tPtr->flags.beveled = True;
205 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
206 tPtr->offsetWidth =
207 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
209 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
210 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
211 handleTextFieldActionEvents, tPtr);
213 tPtr->flags.cursorOn = 1;
215 return tPtr;
219 void
220 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
222 int len;
224 CHECK_CLASS(tPtr, WC_TextField);
226 if (!text)
227 return;
229 len = strlen(text);
231 /* check if buffer will hold the text */
232 if (len + tPtr->textLen >= tPtr->bufferSize) {
233 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
234 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
237 if (position < 0 || position >= tPtr->textLen) {
238 /* append the text at the end */
239 strcat(tPtr->text, text);
241 incrToFit(tPtr);
243 tPtr->textLen += len;
244 tPtr->cursorPosition += len;
245 } else {
246 /* insert text at position */
247 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
248 tPtr->textLen-position+1);
250 memcpy(&(tPtr->text[position]), text, len);
252 tPtr->textLen += len;
253 if (position >= tPtr->cursorPosition) {
254 tPtr->cursorPosition += len;
255 incrToFit2(tPtr);
256 } else {
257 incrToFit(tPtr);
261 paintTextField(tPtr);
263 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
264 (void*)WMInsertTextEvent);
268 static void
269 deleteTextFieldRange(WMTextField *tPtr, WMRange range)
271 CHECK_CLASS(tPtr, WC_TextField);
273 if (range.position >= tPtr->textLen)
274 return;
276 if (range.count < 1) {
277 if (range.position < 0)
278 range.position = 0;
279 tPtr->text[range.position] = 0;
280 tPtr->textLen = range.position;
282 tPtr->cursorPosition = 0;
283 tPtr->viewPosition = 0;
284 } else {
285 if (range.position + range.count > tPtr->textLen)
286 range.count = tPtr->textLen - range.position;
287 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
288 tPtr->textLen - (range.position+range.count) + 1);
289 tPtr->textLen -= range.count;
291 if (tPtr->cursorPosition > range.position)
292 tPtr->cursorPosition -= range.count;
294 decrToFit(tPtr);
297 paintTextField(tPtr);
301 void
302 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
304 deleteTextFieldRange(tPtr, range);
305 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
306 (void*)WMDeleteTextEvent);
311 char*
312 WMGetTextFieldText(WMTextField *tPtr)
314 CHECK_CLASS(tPtr, WC_TextField);
316 return wstrdup(tPtr->text);
320 void
321 WMSetTextFieldText(WMTextField *tPtr, char *text)
323 if ((text && strcmp(tPtr->text, text) == 0) ||
324 (!text && tPtr->textLen == 0))
325 return;
327 if (text==NULL) {
328 tPtr->text[0] = 0;
329 tPtr->textLen = 0;
330 } else {
331 tPtr->textLen = strlen(text);
333 if (tPtr->textLen >= tPtr->bufferSize) {
334 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
335 tPtr->text = realloc(tPtr->text, tPtr->bufferSize);
337 strcpy(tPtr->text, text);
340 if (tPtr->textLen < tPtr->cursorPosition)
341 tPtr->cursorPosition = tPtr->textLen;
343 tPtr->cursorPosition = tPtr->textLen;
344 tPtr->viewPosition = 0;
345 tPtr->selection.count = 0;
347 if (tPtr->view->flags.realized)
348 paintTextField(tPtr);
350 WMPostNotificationName(WMTextDidChangeNotification, tPtr,
351 (void*)WMSetTextEvent);
355 void
356 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
358 /* TODO: update font change after field is mapped */
359 WMReleaseFont(tPtr->font);
360 tPtr->font = WMRetainFont(font);
363 void
364 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
366 tPtr->flags.alignment = alignment;
367 if (alignment!=WALeft) {
368 wwarning("only left alignment is supported in textfields");
369 return;
372 if (tPtr->view->flags.realized) {
373 paintTextField(tPtr);
378 void
379 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
381 tPtr->flags.bordered = bordered;
383 if (tPtr->view->flags.realized) {
384 paintTextField(tPtr);
389 void
390 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
392 tPtr->flags.beveled = flag;
394 if (tPtr->view->flags.realized) {
395 paintTextField(tPtr);
401 void
402 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
404 tPtr->flags.secure = flag;
406 if (tPtr->view->flags.realized) {
407 paintTextField(tPtr);
412 Bool
413 WMGetTextFieldEditable(WMTextField *tPtr)
415 return tPtr->flags.enabled;
419 void
420 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
422 tPtr->flags.enabled = flag;
424 if (tPtr->view->flags.realized) {
425 paintTextField(tPtr);
430 void
431 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
433 if (tPtr->flags.enabled) {
434 if (range.position < 0) {
435 range.count += range.position;
436 range.count = (range.count < 0) ? 0 : range.count;
437 range.position = 0;
438 } else if (range.position > tPtr->textLen) {
439 range.position = tPtr->textLen;
440 range.count = 0;
443 if (range.position + range.count > tPtr->textLen)
444 range.count = tPtr->textLen - range.position;
446 tPtr->prevselection = tPtr->selection; /* check if this is needed */
448 tPtr->selection = range;
450 if (tPtr->view->flags.realized) {
451 paintTextField(tPtr);
457 void
458 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
460 if (tPtr->flags.enabled) {
461 if (position > tPtr->textLen)
462 position = tPtr->textLen;
464 tPtr->cursorPosition = position;
465 if (tPtr->view->flags.realized) {
466 paintTextField(tPtr);
472 void
473 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
475 CHECK_CLASS(tPtr, WC_TextField);
476 if (next == NULL) {
477 if (tPtr->view->nextFocusChain)
478 tPtr->view->nextFocusChain->prevFocusChain = NULL;
479 tPtr->view->nextFocusChain = NULL;
480 return;
483 CHECK_CLASS(next, WC_TextField);
485 if (tPtr->view->nextFocusChain)
486 tPtr->view->nextFocusChain->prevFocusChain = NULL;
487 if (next->view->prevFocusChain)
488 next->view->prevFocusChain->nextFocusChain = NULL;
490 tPtr->view->nextFocusChain = next->view;
491 next->view->prevFocusChain = tPtr->view;
495 void
496 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
498 CHECK_CLASS(tPtr, WC_TextField);
499 if (prev == NULL) {
500 if (tPtr->view->prevFocusChain)
501 tPtr->view->prevFocusChain->nextFocusChain = NULL;
502 tPtr->view->prevFocusChain = NULL;
503 return;
506 CHECK_CLASS(prev, WC_TextField);
508 if (tPtr->view->prevFocusChain)
509 tPtr->view->prevFocusChain->nextFocusChain = NULL;
510 if (prev->view->nextFocusChain)
511 prev->view->nextFocusChain->prevFocusChain = NULL;
513 tPtr->view->prevFocusChain = prev->view;
514 prev->view->nextFocusChain = tPtr->view;
518 static void
519 resizeTextField(WMTextField *tPtr, unsigned int width, unsigned int height)
521 W_ResizeView(tPtr->view, width, height);
523 tPtr->offsetWidth =
524 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
526 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth + 2;
530 static char*
531 makeHiddenString(int length)
533 char *data = wmalloc(length+1);
535 memset(data, '*', length);
536 data[length] = '\0';
537 return data;
541 static void
542 paintCursor(TextField *tPtr)
544 int cx;
545 WMScreen *screen = tPtr->view->screen;
546 int textWidth;
547 char *text;
549 if (tPtr->flags.secure)
550 text = makeHiddenString(strlen(tPtr->text));
551 else
552 text = tPtr->text;
554 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
555 tPtr->cursorPosition-tPtr->viewPosition);
557 switch (tPtr->flags.alignment) {
558 case WARight:
559 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
560 if (textWidth < tPtr->usableWidth)
561 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
562 else
563 cx += tPtr->offsetWidth + 1;
564 break;
565 case WALeft:
566 cx += tPtr->offsetWidth + 1;
567 break;
568 case WAJustified:
569 /* not supported */
570 case WACenter:
571 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
572 if (textWidth < tPtr->usableWidth)
573 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
574 else
575 cx += tPtr->offsetWidth;
576 break;
579 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
580 cx, tPtr->offsetWidth, 1,
581 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
582 printf("%d %d\n",cx,tPtr->cursorPosition);
584 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
585 cx, tPtr->offsetWidth, cx,
586 tPtr->view->size.height - tPtr->offsetWidth - 1);
588 if (tPtr->flags.secure)
589 free(text);
594 static void
595 drawRelief(WMView *view, Bool beveled)
597 WMScreen *scr = view->screen;
598 Display *dpy = scr->display;
599 GC wgc;
600 GC lgc;
601 GC dgc;
602 int width = view->size.width;
603 int height = view->size.height;
605 dgc = WMColorGC(scr->darkGray);
607 if (!beveled) {
608 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
610 return;
612 wgc = WMColorGC(scr->white);
613 lgc = WMColorGC(scr->gray);
615 /* top left */
616 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
617 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
619 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
620 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
622 /* bottom right */
623 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
624 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
626 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
627 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
631 static void
632 paintTextField(TextField *tPtr)
634 W_Screen *screen = tPtr->view->screen;
635 W_View *view = tPtr->view;
636 W_View viewbuffer;
637 int tx, ty, tw, th;
638 int rx;
639 int bd;
640 int totalWidth;
641 char *text;
642 Pixmap drawbuffer;
645 if (!view->flags.realized || !view->flags.mapped)
646 return;
648 if (!tPtr->flags.bordered) {
649 bd = 0;
650 } else {
651 bd = 2;
654 if (tPtr->flags.secure) {
655 text = makeHiddenString(strlen(tPtr->text));
656 } else {
657 text = tPtr->text;
660 totalWidth = tPtr->view->size.width - 2*bd;
662 drawbuffer = XCreatePixmap(screen->display, view->window,
663 view->size.width, view->size.height, screen->depth);
664 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
665 0,0, view->size.width,view->size.height);
666 /* this is quite dirty */
667 viewbuffer.screen = view->screen;
668 viewbuffer.size = view->size;
669 viewbuffer.window = drawbuffer;
672 if (tPtr->textLen > 0) {
673 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
674 tPtr->textLen - tPtr->viewPosition);
676 th = WMFontHeight(tPtr->font);
678 ty = tPtr->offsetWidth;
679 switch (tPtr->flags.alignment) {
680 case WALeft:
681 tx = tPtr->offsetWidth + 1;
682 if (tw < tPtr->usableWidth)
683 XFillRectangle(screen->display, drawbuffer,
684 WMColorGC(screen->white),
685 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
686 break;
688 case WACenter:
689 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
690 if (tw < tPtr->usableWidth)
691 XClearArea(screen->display, view->window, bd, bd,
692 totalWidth, view->size.height-2*bd, False);
693 break;
695 default:
696 case WARight:
697 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
698 if (tw < tPtr->usableWidth)
699 XClearArea(screen->display, view->window, bd, bd,
700 totalWidth-tw, view->size.height-2*bd, False);
701 break;
704 if (!tPtr->flags.enabled)
705 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
707 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
708 tPtr->font, tx, ty,
709 &(text[tPtr->viewPosition]),
710 tPtr->textLen - tPtr->viewPosition);
712 if (tPtr->selection.count) {
713 int count,count2;
715 count = tPtr->selection.count < 0
716 ? tPtr->selection.position + tPtr->selection.count
717 : tPtr->selection.position;
718 count2 = abs(tPtr->selection.count);
719 if (count < tPtr->viewPosition) {
720 count2 = abs(count2 - abs(tPtr->viewPosition - count));
721 count = tPtr->viewPosition;
725 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
726 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
728 XSetBackground(screen->display, screen->textFieldGC,
729 screen->gray->color.pixel);
731 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
732 tPtr->font, rx, ty, &(text[count]),
733 abs(tPtr->selection.count));
735 XSetBackground(screen->display, screen->textFieldGC,
736 screen->white->color.pixel);
739 if (!tPtr->flags.enabled)
740 WMSetColorInGC(screen->black, screen->textFieldGC);
741 } else {
742 XFillRectangle(screen->display, drawbuffer,
743 WMColorGC(screen->white),
744 bd,bd, totalWidth,view->size.height-2*bd);
747 /* draw relief */
748 if (tPtr->flags.bordered) {
749 drawRelief(&viewbuffer, tPtr->flags.beveled);
752 if (tPtr->flags.secure)
753 free(text);
754 XCopyArea(screen->display, drawbuffer, view->window,
755 screen->copyGC, 0,0, view->size.width,
756 view->size.height,0,0);
757 XFreePixmap(screen->display, drawbuffer);
759 /* draw cursor */
760 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
761 paintCursor(tPtr);
766 #if 0
767 static void
768 blinkCursor(void *data)
770 TextField *tPtr = (TextField*)data;
772 if (tPtr->flags.cursorOn) {
773 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
774 data);
775 } else {
776 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
777 data);
779 paintCursor(tPtr);
780 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
782 #endif
785 static void
786 handleEvents(XEvent *event, void *data)
788 TextField *tPtr = (TextField*)data;
790 CHECK_CLASS(data, WC_TextField);
793 switch (event->type) {
794 case FocusIn:
795 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
796 return;
797 tPtr->flags.focused = 1;
798 #if 0
799 if (!tPtr->timerID) {
800 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
801 blinkCursor, tPtr);
803 #endif
804 paintTextField(tPtr);
806 WMPostNotificationName(WMTextDidBeginEditingNotification, tPtr, NULL);
808 tPtr->flags.notIllegalMovement = 0;
809 break;
811 case FocusOut:
812 tPtr->flags.focused = 0;
813 #if 0
814 if (tPtr->timerID)
815 WMDeleteTimerHandler(tPtr->timerID);
816 tPtr->timerID = NULL;
817 #endif
819 paintTextField(tPtr);
820 if (!tPtr->flags.notIllegalMovement) {
821 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
822 (void*)WMIllegalTextMovement);
824 break;
826 case Expose:
827 if (event->xexpose.count!=0)
828 break;
829 paintTextField(tPtr);
830 break;
832 case DestroyNotify:
833 destroyTextField(tPtr);
834 break;
839 static void
840 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
842 char buffer[64];
843 KeySym ksym;
844 int count, refresh = 0;
845 int control_pressed = 0;
847 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK) {
848 control_pressed = 1;
851 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
852 buffer[count] = '\0';
854 if (!(event->xkey.state & ShiftMask)) {
855 if (tPtr->selection.count)
856 refresh = 1;
857 tPtr->prevselection = tPtr->selection;
858 tPtr->selection.position = tPtr->cursorPosition;
859 tPtr->selection.count = 0;
862 /* Be careful in any case in this switch statement, never to call
863 * to more than a function that can generate text change notifications.
864 * Only one text change notification should be sent in any case.
865 * Else hazardous things can happen.
866 * Maybe we need a better solution than the function wrapper to inform
867 * functions that change text in text fields, if they need to send a
868 * change notification or not. -Dan
870 switch (ksym) {
871 case XK_Tab:
872 if (event->xkey.state & ShiftMask) {
873 if (tPtr->view->prevFocusChain) {
874 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
875 tPtr->view->prevFocusChain);
876 tPtr->flags.notIllegalMovement = 1;
878 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
879 (void*)WMBacktabTextMovement);
880 } else {
881 if (tPtr->view->nextFocusChain) {
882 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
883 tPtr->view->nextFocusChain);
884 tPtr->flags.notIllegalMovement = 1;
886 WMPostNotificationName(WMTextDidEndEditingNotification,
887 tPtr, (void*)WMTabTextMovement);
889 break;
891 case XK_Return:
892 WMPostNotificationName(WMTextDidEndEditingNotification, tPtr,
893 (void*)WMReturnTextMovement);
894 break;
896 case WM_EMACSKEY_LEFT:
897 if (!control_pressed) {
898 goto normal_key;
900 case XK_KP_Left:
901 case XK_Left:
902 if (tPtr->cursorPosition > 0) {
903 paintCursor(tPtr);
904 if (event->xkey.state & ControlMask) {
905 int i;
906 for (i = tPtr->cursorPosition - 1; i >= 0; i--)
907 if (tPtr->text[i] == ' ' || i == 0) {
908 tPtr->cursorPosition = i;
909 break;
911 } else {
912 tPtr->cursorPosition--;
914 if (tPtr->cursorPosition < tPtr->viewPosition) {
915 tPtr->viewPosition = tPtr->cursorPosition;
916 refresh = 1;
917 } else {
918 paintCursor(tPtr);
921 break;
923 case WM_EMACSKEY_RIGHT:
924 if (!control_pressed) {
925 goto normal_key;
927 case XK_KP_Right:
928 case XK_Right:
929 if (tPtr->cursorPosition < tPtr->textLen) {
930 paintCursor(tPtr);
931 if (event->xkey.state & ControlMask) {
932 int i;
933 for (i = tPtr->cursorPosition + 1; i <= tPtr->textLen; i++)
934 if (tPtr->text[i] == ' ' || i == tPtr->textLen) {
935 tPtr->cursorPosition = i;
936 break;
938 } else {
939 tPtr->cursorPosition++;
941 while (WMWidthOfString(tPtr->font,
942 &(tPtr->text[tPtr->viewPosition]),
943 tPtr->cursorPosition-tPtr->viewPosition)
944 > tPtr->usableWidth) {
945 tPtr->viewPosition++;
946 refresh = 1;
948 if (!refresh)
949 paintCursor(tPtr);
951 break;
953 case WM_EMACSKEY_HOME:
954 if (!control_pressed) {
955 goto normal_key;
957 case XK_KP_Home:
958 case XK_Home:
959 if (tPtr->cursorPosition > 0) {
960 paintCursor(tPtr);
961 tPtr->cursorPosition = 0;
962 if (tPtr->viewPosition > 0) {
963 tPtr->viewPosition = 0;
964 refresh = 1;
965 } else {
966 paintCursor(tPtr);
969 break;
971 case WM_EMACSKEY_END:
972 if (!control_pressed) {
973 goto normal_key;
975 case XK_KP_End:
976 case XK_End:
977 if (tPtr->cursorPosition < tPtr->textLen) {
978 paintCursor(tPtr);
979 tPtr->cursorPosition = tPtr->textLen;
980 tPtr->viewPosition = 0;
981 while (WMWidthOfString(tPtr->font,
982 &(tPtr->text[tPtr->viewPosition]),
983 tPtr->textLen-tPtr->viewPosition)
984 > tPtr->usableWidth) {
985 tPtr->viewPosition++;
986 refresh = 1;
988 if (!refresh)
989 paintCursor(tPtr);
991 break;
993 case WM_EMACSKEY_BS:
994 if (!control_pressed) {
995 goto normal_key;
997 case XK_BackSpace:
998 if (tPtr->cursorPosition > 0) {
999 WMRange range;
1001 if (tPtr->prevselection.count) {
1002 range.position = tPtr->prevselection.count < 0
1003 ? tPtr->prevselection.position + tPtr->prevselection.count
1004 : tPtr->prevselection.position;
1006 range.count = abs(tPtr->prevselection.count);
1007 } else {
1008 range.position = tPtr->cursorPosition - 1;
1009 range.count = 1;
1011 WMDeleteTextFieldRange(tPtr, range);
1013 break;
1015 case WM_EMACSKEY_DEL:
1016 if (!control_pressed) {
1017 goto normal_key;
1019 case XK_KP_Delete:
1020 case XK_Delete:
1021 if (tPtr->cursorPosition < tPtr->textLen || tPtr->prevselection.count) {
1022 WMRange range;
1024 if (tPtr->prevselection.count) {
1025 range.position = tPtr->prevselection.count < 0
1026 ? tPtr->prevselection.position + tPtr->prevselection.count
1027 : tPtr->prevselection.position;
1029 range.count = abs(tPtr->prevselection.count);
1030 } else {
1031 range.position = tPtr->cursorPosition;
1032 range.count = 1;
1034 WMDeleteTextFieldRange(tPtr, range);
1036 break;
1038 normal_key:
1039 default:
1040 if (count > 0 && !iscntrl(buffer[0])) {
1041 WMRange range;
1043 if (tPtr->prevselection.count) {
1044 range.position = tPtr->prevselection.count < 0
1045 ? tPtr->prevselection.position + tPtr->prevselection.count
1046 : tPtr->prevselection.position;
1048 range.count = abs(tPtr->prevselection.count);
1049 } else {
1050 range.position = tPtr->cursorPosition;
1051 range.count = 1;
1053 if (tPtr->prevselection.count)
1054 deleteTextFieldRange(tPtr, range);
1055 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1056 } else {
1057 return;
1059 break;
1061 if (event->xkey.state & ShiftMask) {
1062 if (tPtr->selection.count == 0)
1063 tPtr->selection.position = tPtr->cursorPosition;
1064 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1065 refresh = 1;
1067 tPtr->prevselection.count = 0;
1068 if (refresh) {
1069 paintTextField(tPtr);
1074 static int
1075 pointToCursorPosition(TextField *tPtr, int x)
1077 int a, b, mid;
1078 int tw;
1080 if (tPtr->flags.bordered)
1081 x -= 2;
1083 a = tPtr->viewPosition;
1084 b = tPtr->viewPosition + tPtr->textLen;
1085 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1086 tPtr->textLen-tPtr->viewPosition) < x)
1087 return tPtr->textLen;
1089 while (a < b && b-a>1) {
1090 mid = (a+b)/2;
1091 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1092 mid - tPtr->viewPosition);
1093 if (tw > x)
1094 b = mid;
1095 else if (tw < x)
1096 a = mid;
1097 else
1098 return mid;
1100 return (a+b)/2;
1104 static void
1105 handleTextFieldActionEvents(XEvent *event, void *data)
1107 TextField *tPtr = (TextField*)data;
1108 static int move;
1110 CHECK_CLASS(data, WC_TextField);
1112 switch (event->type) {
1113 case KeyPress:
1114 if (tPtr->flags.enabled && tPtr->flags.focused)
1115 handleTextFieldKeyPress(tPtr, event);
1117 break;
1119 case MotionNotify:
1120 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1123 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1124 tPtr->usableWidth) {
1125 if (WMWidthOfString(tPtr->font,
1126 &(tPtr->text[tPtr->viewPosition]),
1127 tPtr->cursorPosition-tPtr->viewPosition)
1128 > tPtr->usableWidth) {
1129 tPtr->viewPosition++;
1132 else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1133 paintCursor(tPtr);
1134 tPtr->viewPosition--;
1137 if (!tPtr->selection.count) {
1138 tPtr->selection.position = tPtr->cursorPosition;
1141 tPtr->cursorPosition = pointToCursorPosition(tPtr, event->xmotion.x);
1143 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1146 printf("notify %d %d\n",event->xmotion.x,tPtr->usableWidth);
1149 paintCursor(tPtr);
1150 paintTextField(tPtr);
1153 if (move) {
1154 int count;
1155 XSetSelectionOwner(tPtr->view->screen->display,
1156 XA_PRIMARY, None, CurrentTime);
1157 count = tPtr->selection.count < 0
1158 ? tPtr->selection.position + tPtr->selection.count
1159 : tPtr->selection.position;
1160 XStoreBuffer(tPtr->view->screen->display,
1161 &tPtr->text[count] , abs(tPtr->selection.count), 0);
1163 break;
1165 case ButtonPress:
1166 move = 1;
1167 switch (tPtr->flags.alignment) {
1168 int textWidth;
1169 case WARight:
1170 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1171 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1172 WMSetFocusToWidget(tPtr);
1173 } else if (tPtr->flags.focused) {
1174 tPtr->selection.count = 0;
1176 if(textWidth < tPtr->usableWidth){
1177 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1178 event->xbutton.x - tPtr->usableWidth
1179 + textWidth);
1181 else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1182 event->xbutton.x);
1184 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1185 event->xbutton.x);
1186 tPtr->cursorPosition += tPtr->usableWidth - textWidth;
1189 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1190 event->xbutton.x);
1192 paintTextField(tPtr);
1193 break;
1195 case WALeft:
1196 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1197 WMSetFocusToWidget(tPtr);
1198 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1199 event->xbutton.x);
1200 paintTextField(tPtr);
1201 } else if (tPtr->flags.focused) {
1202 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1203 event->xbutton.x);
1204 tPtr->selection.count = 0;
1205 paintTextField(tPtr);
1207 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1208 char *text;
1210 text = W_GetTextSelection(tPtr->view->screen,
1211 tPtr->view->screen->clipboardAtom);
1212 if (!text) {
1213 text = W_GetTextSelection(tPtr->view->screen, XA_CUT_BUFFER0);
1215 if (text) {
1216 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1217 XFree(text);
1218 WMPostNotificationName(WMTextDidChangeNotification, tPtr, NULL);
1221 break;
1223 break;
1225 case ButtonRelease:
1226 move = 0;
1227 break;
1232 static void
1233 destroyTextField(TextField *tPtr)
1235 #if 0
1236 if (tPtr->timerID)
1237 WMDeleteTimerHandler(tPtr->timerID);
1238 #endif
1240 WMReleaseFont(tPtr->font);
1242 if (tPtr->text)
1243 free(tPtr->text);
1245 free(tPtr);