Fixed paths, because mr Alex "Big Mouth" Perez was unable to get them right
[wmaker-crm.git] / WINGs / wtextfield.c
blob6090c0c056646918785af79ebeacb92ad3285e1b
5 #include "WINGsP.h"
6 #include "wconfig.h"
8 #include <X11/keysym.h>
9 #include <X11/Xatom.h>
11 #include <ctype.h>
13 #define CURSOR_BLINK_ON_DELAY 600
14 #define CURSOR_BLINK_OFF_DELAY 300
18 char *WMTextDidChangeNotification = "WMTextDidChangeNotification";
19 char *WMTextDidBeginEditingNotification = "WMTextDidBeginEditingNotification";
20 char *WMTextDidEndEditingNotification = "WMTextDidEndEditingNotification";
23 typedef struct W_TextField {
24 W_Class widgetClass;
25 W_View *view;
27 #if 0
28 struct W_TextField *nextField; /* next textfield in the chain */
29 struct W_TextField *prevField;
30 #endif
32 char *text;
33 int textLen; /* size of text */
34 int bufferSize; /* memory allocated for text */
36 int viewPosition; /* position of text being shown */
38 int cursorPosition; /* position of the insertion cursor */
40 short usableWidth;
41 short offsetWidth; /* offset of text from border */
43 WMRange selection;
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 unsigned int ownsSelection:1;
71 unsigned int waitingSelection:1; /* requested selection, but
72 * didnt get yet */
75 unsigned int notIllegalMovement:1;
76 } flags;
77 } TextField;
80 #define NOTIFY(T,C,N,A) { WMNotification *notif = WMCreateNotification(N,T,A);\
81 if ((T)->delegate && (T)->delegate->C)\
82 (*(T)->delegate->C)((T)->delegate,notif);\
83 WMPostNotification(notif);\
84 WMReleaseNotification(notif);}
87 #define MIN_TEXT_BUFFER 2
88 #define TEXT_BUFFER_INCR 8
91 #define WM_EMACSKEYMASK ControlMask
93 #define WM_EMACSKEY_LEFT XK_b
94 #define WM_EMACSKEY_RIGHT XK_f
95 #define WM_EMACSKEY_HOME XK_a
96 #define WM_EMACSKEY_END XK_e
97 #define WM_EMACSKEY_BS XK_h
98 #define WM_EMACSKEY_DEL XK_d
102 #define DEFAULT_WIDTH 60
103 #define DEFAULT_HEIGHT 20
104 #define DEFAULT_BORDERED True
105 #define DEFAULT_ALIGNMENT WALeft
109 static void destroyTextField(TextField *tPtr);
110 static void paintTextField(TextField *tPtr);
112 static void handleEvents(XEvent *event, void *data);
113 static void handleTextFieldActionEvents(XEvent *event, void *data);
114 static void didResizeTextField();
116 struct W_ViewDelegate _TextFieldViewDelegate = {
117 NULL,
118 NULL,
119 didResizeTextField,
120 NULL,
121 NULL
126 static void lostSelection(WMView *view, Atom selection, void *cdata);
128 static WMData *requestHandler(WMView *view, Atom selection, Atom target,
129 void *cdata, Atom *type);
132 static WMSelectionProcs selectionHandler = {
133 requestHandler,
134 lostSelection,
135 NULL
139 #define TEXT_WIDTH(tPtr, start) (WMWidthOfString((tPtr)->font, \
140 &((tPtr)->text[(start)]), (tPtr)->textLen - (start) + 1))
142 #define TEXT_WIDTH2(tPtr, start, end) (WMWidthOfString((tPtr)->font, \
143 &((tPtr)->text[(start)]), (end) - (start) + 1))
146 static void
147 normalizeRange(TextField *tPtr, WMRange *range)
149 if (range->position < 0 && range->count < 0)
150 range->count = 0;
152 if (range->count == 0) {
153 /*range->position = 0; why is this?*/
154 return;
157 /* (1,-2) ~> (0,1) ; (1,-1) ~> (0,1) ; (2,-1) ~> (1,1) */
158 if (range->count < 0) { /* && range->position >= 0 */
159 if (range->position + range->count < 0) {
160 range->count = range->position;
161 range->position = 0;
162 } else {
163 range->count = -range->count;
164 range->position -= range->count;
166 /* (-2,1) ~> (0,0) ; (-1,1) ~> (0,0) ; (-1,2) ~> (0,1) */
167 } else if (range->position < 0) { /* && range->count > 0 */
168 if (range->position + range->count < 0) {
169 range->position = range->count = 0;
170 } else {
171 range->count += range->position;
172 range->position = 0;
176 if (range->position + range->count > tPtr->textLen)
177 range->count = tPtr->textLen - range->position;
180 static void
181 memmv(char *dest, char *src, int size)
183 int i;
185 if (dest > src) {
186 for (i=size-1; i>=0; i--) {
187 dest[i] = src[i];
189 } else if (dest < src) {
190 for (i=0; i<size; i++) {
191 dest[i] = src[i];
197 static int
198 incrToFit(TextField *tPtr)
200 int vp = tPtr->viewPosition;
202 while (TEXT_WIDTH(tPtr, tPtr->viewPosition) > tPtr->usableWidth) {
203 tPtr->viewPosition++;
205 return vp!=tPtr->viewPosition;
209 static int
210 incrToFit2(TextField *tPtr)
212 int vp = tPtr->viewPosition;
213 while (TEXT_WIDTH2(tPtr, tPtr->viewPosition, tPtr->cursorPosition)
214 >= tPtr->usableWidth)
215 tPtr->viewPosition++;
218 return vp!=tPtr->viewPosition;
222 static void
223 decrToFit(TextField *tPtr)
225 while (TEXT_WIDTH(tPtr, tPtr->viewPosition-1) < tPtr->usableWidth
226 && tPtr->viewPosition>0)
227 tPtr->viewPosition--;
230 #undef TEXT_WIDTH
231 #undef TEXT_WIDTH2
235 static WMData*
236 requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
237 Atom *type)
239 TextField *tPtr = view->self;
240 int count;
241 Display *dpy = tPtr->view->screen->display;
242 Atom _TARGETS;
243 Atom TEXT = XInternAtom(dpy, "TEXT", False);
244 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
245 WMData *data;
247 count = tPtr->selection.count < 0
248 ? tPtr->selection.position + tPtr->selection.count
249 : tPtr->selection.position;
251 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
253 data = WMCreateDataWithBytes(&(tPtr->text[count]),
254 abs(tPtr->selection.count));
255 WMSetDataFormat(data, 8);
256 *type = target;
258 return data;
261 _TARGETS = XInternAtom(dpy, "TARGETS", False);
262 if (target == _TARGETS) {
263 Atom *ptr;
265 ptr = wmalloc(4 * sizeof(Atom));
266 ptr[0] = _TARGETS;
267 ptr[1] = XA_STRING;
268 ptr[2] = TEXT;
269 ptr[3] = COMPOUND_TEXT;
271 data = WMCreateDataWithBytes(ptr, 4*4);
272 WMSetDataFormat(data, 32);
274 *type = target;
275 return data;
278 return NULL;
283 static void
284 lostSelection(WMView *view, Atom selection, void *cdata)
286 TextField *tPtr = (WMTextField*)view->self;
288 if (tPtr->flags.ownsSelection) {
289 WMDeleteSelectionHandler(view, selection, CurrentTime);
290 tPtr->flags.ownsSelection = 0;
292 if (tPtr->selection.count != 0) {
293 tPtr->selection.count = 0;
294 paintTextField(tPtr);
299 static void
300 selectionNotification(void *observerData, WMNotification *notification)
302 WMView *observerView = (WMView*)observerData;
303 WMView *newOwnerView = (WMView*)WMGetNotificationClientData(notification);
305 if (observerView != newOwnerView) {
307 //if (tPtr->flags.ownsSelection)
308 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
310 lostSelection(observerView, XA_PRIMARY, NULL);
315 static void
316 realizeObserver(void *self, WMNotification *not)
318 W_CreateIC(((TextField*)self)->view);
322 WMTextField*
323 WMCreateTextField(WMWidget *parent)
325 TextField *tPtr;
327 tPtr = wmalloc(sizeof(TextField));
328 memset(tPtr, 0, sizeof(TextField));
330 tPtr->widgetClass = WC_TextField;
332 tPtr->view = W_CreateView(W_VIEW(parent));
333 if (!tPtr->view) {
334 wfree(tPtr);
335 return NULL;
337 tPtr->view->self = tPtr;
339 tPtr->view->delegate = &_TextFieldViewDelegate;
341 tPtr->view->attribFlags |= CWCursor;
342 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
344 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
346 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
347 tPtr->text[0] = 0;
348 tPtr->textLen = 0;
349 tPtr->bufferSize = MIN_TEXT_BUFFER;
351 tPtr->flags.enabled = 1;
353 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
354 |FocusChangeMask, handleEvents, tPtr);
356 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
358 tPtr->flags.bordered = DEFAULT_BORDERED;
359 tPtr->flags.beveled = True;
360 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
361 tPtr->offsetWidth =
362 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
364 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
366 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
367 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
368 handleTextFieldActionEvents, tPtr);
370 WMAddNotificationObserver(selectionNotification, tPtr->view,
371 WMSelectionOwnerDidChangeNotification,
372 (void*)XA_PRIMARY);
374 WMAddNotificationObserver(realizeObserver, tPtr,
375 WMViewRealizedNotification, tPtr->view);
377 tPtr->flags.cursorOn = 1;
379 return tPtr;
383 void
384 WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
386 CHECK_CLASS(tPtr, WC_TextField);
388 tPtr->delegate = delegate;
392 WMTextFieldDelegate*
393 WMGetTextFieldDelegate(WMTextField *tPtr)
395 CHECK_CLASS(tPtr, WC_TextField);
397 return tPtr->delegate;
401 void
402 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
404 int len;
406 CHECK_CLASS(tPtr, WC_TextField);
408 if (!text)
409 return;
411 len = strlen(text);
413 /* check if buffer will hold the text */
414 if (len + tPtr->textLen >= tPtr->bufferSize) {
415 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
416 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
419 if (position < 0 || position >= tPtr->textLen) {
420 /* append the text at the end */
421 strcat(tPtr->text, text);
423 incrToFit(tPtr);
425 tPtr->textLen += len;
426 tPtr->cursorPosition += len;
427 } else {
428 /* insert text at position */
429 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
430 tPtr->textLen-position+1);
432 memcpy(&(tPtr->text[position]), text, len);
434 tPtr->textLen += len;
435 if (position >= tPtr->cursorPosition) {
436 tPtr->cursorPosition += len;
437 incrToFit2(tPtr);
438 } else {
439 incrToFit(tPtr);
443 paintTextField(tPtr);
446 void
447 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
449 CHECK_CLASS(tPtr, WC_TextField);
451 normalizeRange(tPtr, &range);
453 if (!range.count)
454 return;
456 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
457 tPtr->textLen - (range.position+range.count) + 1);
459 tPtr->textLen -= range.count;
461 /* try to keep cursorPosition at the same place */
462 tPtr->viewPosition -= range.count;
463 if (tPtr->viewPosition < 0)
464 tPtr->viewPosition = 0;
465 tPtr->cursorPosition = range.position;
467 decrToFit(tPtr);
469 paintTextField(tPtr);
474 char*
475 WMGetTextFieldText(WMTextField *tPtr)
477 CHECK_CLASS(tPtr, WC_TextField);
479 return wstrdup(tPtr->text);
483 void
484 WMSetTextFieldText(WMTextField *tPtr, char *text)
486 CHECK_CLASS(tPtr, WC_TextField);
488 if ((text && strcmp(tPtr->text, text) == 0) ||
489 (!text && tPtr->textLen == 0))
490 return;
492 if (text==NULL) {
493 tPtr->text[0] = 0;
494 tPtr->textLen = 0;
495 } else {
496 tPtr->textLen = strlen(text);
498 if (tPtr->textLen >= tPtr->bufferSize) {
499 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
500 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
502 strcpy(tPtr->text, text);
505 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
506 tPtr->viewPosition = 0;
507 tPtr->selection.count = 0;
509 if (tPtr->view->flags.realized)
510 paintTextField(tPtr);
514 void
515 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
517 CHECK_CLASS(tPtr, WC_TextField);
519 tPtr->flags.alignment = alignment;
521 if (alignment!=WALeft) {
522 wwarning("only left alignment is supported in textfields");
523 return;
526 if (tPtr->view->flags.realized) {
527 paintTextField(tPtr);
532 void
533 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
535 CHECK_CLASS(tPtr, WC_TextField);
537 tPtr->flags.bordered = bordered;
539 if (tPtr->view->flags.realized) {
540 paintTextField(tPtr);
545 void
546 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
548 CHECK_CLASS(tPtr, WC_TextField);
550 tPtr->flags.beveled = ((flag==0) ? 0 : 1);
552 if (tPtr->view->flags.realized) {
553 paintTextField(tPtr);
559 void
560 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
562 CHECK_CLASS(tPtr, WC_TextField);
564 tPtr->flags.secure = ((flag==0) ? 0 : 1);
566 if (tPtr->view->flags.realized) {
567 paintTextField(tPtr);
572 Bool
573 WMGetTextFieldEditable(WMTextField *tPtr)
575 CHECK_CLASS(tPtr, WC_TextField);
577 return tPtr->flags.enabled;
581 void
582 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
584 CHECK_CLASS(tPtr, WC_TextField);
586 tPtr->flags.enabled = ((flag==0) ? 0 : 1);
588 if (tPtr->view->flags.realized) {
589 paintTextField(tPtr);
594 void
595 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
597 CHECK_CLASS(tPtr, WC_TextField);
599 if (tPtr->flags.enabled) {
600 normalizeRange(tPtr, &range);
602 tPtr->selection = range;
604 tPtr->cursorPosition = range.position + range.count;
606 if (tPtr->view->flags.realized) {
607 paintTextField(tPtr);
613 void
614 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
616 CHECK_CLASS(tPtr, WC_TextField);
618 if (tPtr->flags.enabled) {
619 if (position > tPtr->textLen)
620 position = tPtr->textLen;
622 tPtr->cursorPosition = position;
623 if (tPtr->view->flags.realized) {
624 paintTextField(tPtr);
630 void
631 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
633 CHECK_CLASS(tPtr, WC_TextField);
634 if (next == NULL) {
635 if (tPtr->view->nextFocusChain)
636 tPtr->view->nextFocusChain->prevFocusChain = NULL;
637 tPtr->view->nextFocusChain = NULL;
638 return;
641 CHECK_CLASS(next, WC_TextField);
643 if (tPtr->view->nextFocusChain)
644 tPtr->view->nextFocusChain->prevFocusChain = NULL;
645 if (next->view->prevFocusChain)
646 next->view->prevFocusChain->nextFocusChain = NULL;
648 tPtr->view->nextFocusChain = next->view;
649 next->view->prevFocusChain = tPtr->view;
653 void
654 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
656 CHECK_CLASS(tPtr, WC_TextField);
657 if (prev == NULL) {
658 if (tPtr->view->prevFocusChain)
659 tPtr->view->prevFocusChain->nextFocusChain = NULL;
660 tPtr->view->prevFocusChain = NULL;
661 return;
664 CHECK_CLASS(prev, WC_TextField);
666 if (tPtr->view->prevFocusChain)
667 tPtr->view->prevFocusChain->nextFocusChain = NULL;
668 if (prev->view->nextFocusChain)
669 prev->view->nextFocusChain->prevFocusChain = NULL;
671 tPtr->view->prevFocusChain = prev->view;
672 prev->view->nextFocusChain = tPtr->view;
676 void
677 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
679 CHECK_CLASS(tPtr, WC_TextField);
681 if (tPtr->font)
682 WMReleaseFont(tPtr->font);
683 tPtr->font = WMRetainFont(font);
685 tPtr->offsetWidth =
686 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
688 if (tPtr->view->flags.realized) {
689 paintTextField(tPtr);
695 WMFont*
696 WMGetTextFieldFont(WMTextField *tPtr)
698 return tPtr->font;
702 static void
703 didResizeTextField(W_ViewDelegate *self, WMView *view)
705 WMTextField *tPtr = (WMTextField*)view->self;
707 tPtr->offsetWidth =
708 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
710 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth /*+ 2*/;
714 static char*
715 makeHiddenString(int length)
717 char *data = wmalloc(length+1);
719 memset(data, '*', length);
720 data[length] = '\0';
722 return data;
726 static void
727 paintCursor(TextField *tPtr)
729 int cx;
730 WMScreen *screen = tPtr->view->screen;
731 int textWidth;
732 char *text;
734 if (tPtr->flags.secure)
735 text = makeHiddenString(strlen(tPtr->text));
736 else
737 text = tPtr->text;
739 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
740 tPtr->cursorPosition-tPtr->viewPosition);
742 switch (tPtr->flags.alignment) {
743 case WARight:
744 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
745 if (textWidth < tPtr->usableWidth)
746 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
747 else
748 cx += tPtr->offsetWidth + 1;
749 break;
750 case WALeft:
751 cx += tPtr->offsetWidth + 1;
752 break;
753 case WAJustified:
754 /* not supported */
755 case WACenter:
756 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
757 if (textWidth < tPtr->usableWidth)
758 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
759 else
760 cx += tPtr->offsetWidth;
761 break;
764 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
765 cx, tPtr->offsetWidth, 1,
766 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
767 printf("%d %d\n",cx,tPtr->cursorPosition);
770 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
771 cx, tPtr->offsetWidth, cx,
772 tPtr->view->size.height - tPtr->offsetWidth - 1);
774 W_SetPreeditPositon(tPtr->view, cx, 0);
776 if (tPtr->flags.secure) {
777 wfree(text);
783 static void
784 drawRelief(WMView *view, Bool beveled)
786 WMScreen *scr = view->screen;
787 Display *dpy = scr->display;
788 GC wgc;
789 GC lgc;
790 GC dgc;
791 int width = view->size.width;
792 int height = view->size.height;
794 dgc = WMColorGC(scr->darkGray);
796 if (!beveled) {
797 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
799 return;
801 wgc = WMColorGC(scr->white);
802 lgc = WMColorGC(scr->gray);
804 /* top left */
805 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
806 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
808 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
809 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
811 /* bottom right */
812 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
813 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
815 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
816 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
820 static void
821 paintTextField(TextField *tPtr)
823 W_Screen *screen = tPtr->view->screen;
824 W_View *view = tPtr->view;
825 W_View viewbuffer;
826 int tx, ty, tw, th;
827 int rx;
828 int bd;
829 int totalWidth;
830 char *text;
831 Pixmap drawbuffer;
832 WMColor *color;
835 if (!view->flags.realized || !view->flags.mapped)
836 return;
838 if (!tPtr->flags.bordered) {
839 bd = 0;
840 } else {
841 bd = 2;
844 if (tPtr->flags.secure) {
845 text = makeHiddenString(strlen(tPtr->text));
846 } else {
847 text = tPtr->text;
850 totalWidth = tPtr->view->size.width - 2*bd;
852 drawbuffer = XCreatePixmap(screen->display, view->window,
853 view->size.width, view->size.height, screen->depth);
854 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
855 0,0, view->size.width,view->size.height);
856 /* this is quite dirty */
857 viewbuffer.screen = view->screen;
858 viewbuffer.size = view->size;
859 viewbuffer.window = drawbuffer;
862 if (tPtr->textLen > 0) {
863 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
864 tPtr->textLen - tPtr->viewPosition);
866 th = WMFontHeight(tPtr->font);
868 ty = tPtr->offsetWidth;
869 switch (tPtr->flags.alignment) {
870 case WALeft:
871 tx = tPtr->offsetWidth + 1;
872 if (tw < tPtr->usableWidth)
873 XFillRectangle(screen->display, drawbuffer,
874 WMColorGC(screen->white),
875 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
876 break;
878 case WACenter:
879 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
880 if (tw < tPtr->usableWidth)
881 XClearArea(screen->display, view->window, bd, bd,
882 totalWidth, view->size.height-2*bd, False);
883 break;
885 default:
886 case WARight:
887 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
888 if (tw < tPtr->usableWidth)
889 XClearArea(screen->display, view->window, bd, bd,
890 totalWidth-tw, view->size.height-2*bd, False);
891 break;
894 color = tPtr->flags.enabled ? screen->black : screen->darkGray;
896 WMDrawImageString(screen, drawbuffer, color, screen->white,
897 tPtr->font, tx, ty, &(text[tPtr->viewPosition]),
898 tPtr->textLen - tPtr->viewPosition);
900 if (tPtr->selection.count) {
901 int count,count2;
903 count = tPtr->selection.count < 0
904 ? tPtr->selection.position + tPtr->selection.count
905 : tPtr->selection.position;
906 count2 = abs(tPtr->selection.count);
907 if (count < tPtr->viewPosition) {
908 count2 = abs(count2 - abs(tPtr->viewPosition - count));
909 count = tPtr->viewPosition;
912 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
913 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
915 WMDrawImageString(screen, drawbuffer, color, screen->gray,
916 tPtr->font, rx, ty, &(text[count]), count2);
918 } else {
919 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
920 bd, bd, totalWidth,view->size.height-2*bd);
923 /* draw relief */
924 if (tPtr->flags.bordered) {
925 drawRelief(&viewbuffer, tPtr->flags.beveled);
928 if (tPtr->flags.secure)
929 wfree(text);
930 XCopyArea(screen->display, drawbuffer, view->window,
931 screen->copyGC, 0,0, view->size.width,
932 view->size.height,0,0);
933 XFreePixmap(screen->display, drawbuffer);
935 /* draw cursor */
936 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
937 paintCursor(tPtr);
942 #if 0
943 static void
944 blinkCursor(void *data)
946 TextField *tPtr = (TextField*)data;
948 if (tPtr->flags.cursorOn) {
949 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
950 data);
951 } else {
952 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
953 data);
955 paintCursor(tPtr);
956 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
958 #endif
961 static void
962 handleEvents(XEvent *event, void *data)
964 TextField *tPtr = (TextField*)data;
966 CHECK_CLASS(data, WC_TextField);
968 switch (event->type) {
969 case FocusIn:
970 W_FocusIC(tPtr->view);
971 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
972 return;
973 tPtr->flags.focused = 1;
974 #if 0
975 if (!tPtr->timerID) {
976 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
977 blinkCursor, tPtr);
979 #endif
980 paintTextField(tPtr);
982 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
984 tPtr->flags.notIllegalMovement = 0;
985 break;
987 case FocusOut:
988 W_UnFocusIC(tPtr->view);
989 tPtr->flags.focused = 0;
990 #if 0
991 if (tPtr->timerID)
992 WMDeleteTimerHandler(tPtr->timerID);
993 tPtr->timerID = NULL;
994 #endif
996 paintTextField(tPtr);
997 if (!tPtr->flags.notIllegalMovement) {
998 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
999 (void*)WMIllegalTextMovement);
1001 break;
1003 case Expose:
1004 if (event->xexpose.count!=0)
1005 break;
1006 paintTextField(tPtr);
1007 break;
1009 case DestroyNotify:
1010 destroyTextField(tPtr);
1011 break;
1016 static void
1017 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
1019 char buffer[64];
1020 KeySym ksym;
1021 char *textEvent = NULL;
1022 void *data = NULL;
1023 int count, refresh = 0;
1024 int control_pressed = 0;
1025 int cancelSelection = 1;
1026 Bool shifted, controled, modified;
1027 Bool relay = True;
1029 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1030 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1031 control_pressed = 1;
1033 shifted = (event->xkey.state & ShiftMask ? True : False);
1034 controled = (event->xkey.state & ControlMask ? True : False);
1035 modified = shifted || controled;
1037 count = W_LookupString(tPtr->view, &event->xkey, buffer, 63, &ksym, NULL);
1038 //count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
1039 buffer[count] = '\0';
1041 switch (ksym) {
1042 case XK_Tab:
1043 #ifdef XK_ISO_Left_Tab
1044 case XK_ISO_Left_Tab:
1045 #endif
1046 if (!controled) {
1047 if (shifted) {
1048 if (tPtr->view->prevFocusChain) {
1049 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
1050 tPtr->view->prevFocusChain);
1051 tPtr->flags.notIllegalMovement = 1;
1053 data = (void*)WMBacktabTextMovement;
1054 } else {
1055 if (tPtr->view->nextFocusChain) {
1056 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
1057 tPtr->view->nextFocusChain);
1058 tPtr->flags.notIllegalMovement = 1;
1060 data = (void*)WMTabTextMovement;
1062 textEvent = WMTextDidEndEditingNotification;
1064 cancelSelection = 0;
1066 relay = False;
1068 break;
1070 case XK_Escape:
1071 if (!modified) {
1072 data = (void*)WMEscapeTextMovement;
1073 textEvent = WMTextDidEndEditingNotification;
1075 relay = False;
1077 break;
1079 case XK_Return:
1080 if (!modified) {
1081 data = (void*)WMReturnTextMovement;
1082 textEvent = WMTextDidEndEditingNotification;
1084 relay = False;
1086 break;
1088 case WM_EMACSKEY_LEFT:
1089 if (!control_pressed)
1090 goto normal_key;
1091 else
1092 controled = False;
1094 #ifdef XK_KP_Left
1095 case XK_KP_Left:
1096 #endif
1097 case XK_Left:
1098 if (tPtr->cursorPosition > 0) {
1099 paintCursor(tPtr);
1100 if (controled) {
1101 int i = tPtr->cursorPosition - 1;
1103 while (i > 0 && tPtr->text[i] != ' ') i--;
1104 while (i > 0 && tPtr->text[i] == ' ') i--;
1106 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1107 } else
1108 tPtr->cursorPosition--;
1110 if (tPtr->cursorPosition < tPtr->viewPosition) {
1111 tPtr->viewPosition = tPtr->cursorPosition;
1112 refresh = 1;
1113 } else
1114 paintCursor(tPtr);
1116 if (shifted)
1117 cancelSelection = 0;
1119 relay = False;
1121 break;
1123 case WM_EMACSKEY_RIGHT:
1124 if (!control_pressed)
1125 goto normal_key;
1126 else
1127 controled = False;
1129 #ifdef XK_KP_Right
1130 case XK_KP_Right:
1131 #endif
1132 case XK_Right:
1133 if (tPtr->cursorPosition < tPtr->textLen) {
1134 paintCursor(tPtr);
1135 if (controled) {
1136 int i = tPtr->cursorPosition;
1138 while (tPtr->text[i] && tPtr->text[i] != ' ') i++;
1139 while (tPtr->text[i] == ' ') i++;
1141 tPtr->cursorPosition = i;
1142 } else {
1143 tPtr->cursorPosition++;
1145 while (WMWidthOfString(tPtr->font,
1146 &(tPtr->text[tPtr->viewPosition]),
1147 tPtr->cursorPosition-tPtr->viewPosition)
1148 > tPtr->usableWidth) {
1149 tPtr->viewPosition++;
1150 refresh = 1;
1152 if (!refresh)
1153 paintCursor(tPtr);
1155 if (shifted)
1156 cancelSelection = 0;
1158 relay = False;
1160 break;
1162 case WM_EMACSKEY_HOME:
1163 if (!control_pressed)
1164 goto normal_key;
1165 else
1166 controled = False;
1168 #ifdef XK_KP_Home
1169 case XK_KP_Home:
1170 #endif
1171 case XK_Home:
1172 if (!controled) {
1173 if (tPtr->cursorPosition > 0) {
1174 paintCursor(tPtr);
1175 tPtr->cursorPosition = 0;
1176 if (tPtr->viewPosition > 0) {
1177 tPtr->viewPosition = 0;
1178 refresh = 1;
1179 } else
1180 paintCursor(tPtr);
1182 if (shifted)
1183 cancelSelection = 0;
1185 relay = False;
1187 break;
1189 case WM_EMACSKEY_END:
1190 if (!control_pressed)
1191 goto normal_key;
1192 else
1193 controled = False;
1195 #ifdef XK_KP_End
1196 case XK_KP_End:
1197 #endif
1198 case XK_End:
1199 if (!controled) {
1200 if (tPtr->cursorPosition < tPtr->textLen) {
1201 paintCursor(tPtr);
1202 tPtr->cursorPosition = tPtr->textLen;
1203 tPtr->viewPosition = 0;
1204 while (WMWidthOfString(tPtr->font,
1205 &(tPtr->text[tPtr->viewPosition]),
1206 tPtr->textLen-tPtr->viewPosition)
1207 > tPtr->usableWidth) {
1208 tPtr->viewPosition++;
1209 refresh = 1;
1211 if (!refresh)
1212 paintCursor(tPtr);
1214 if (shifted)
1215 cancelSelection = 0;
1217 relay = False;
1219 break;
1221 case WM_EMACSKEY_BS:
1222 if (!control_pressed)
1223 goto normal_key;
1224 else
1225 modified = False;
1227 case XK_BackSpace:
1228 if (!modified) {
1229 if (tPtr->selection.count) {
1230 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1231 data = (void*)WMDeleteTextEvent;
1232 textEvent = WMTextDidChangeNotification;
1233 } else if (tPtr->cursorPosition > 0) {
1234 WMRange range;
1235 range.position = tPtr->cursorPosition - 1;
1236 range.count = 1;
1237 WMDeleteTextFieldRange(tPtr, range);
1238 data = (void*)WMDeleteTextEvent;
1239 textEvent = WMTextDidChangeNotification;
1242 relay = False;
1244 break;
1246 case WM_EMACSKEY_DEL:
1247 if (!control_pressed)
1248 goto normal_key;
1249 else
1250 modified = False;
1252 #ifdef XK_KP_Delete
1253 case XK_KP_Delete:
1254 #endif
1255 case XK_Delete:
1256 if (!modified) {
1257 if (tPtr->selection.count) {
1258 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1259 data = (void*)WMDeleteTextEvent;
1260 textEvent = WMTextDidChangeNotification;
1261 } else if (tPtr->cursorPosition < tPtr->textLen) {
1262 WMRange range;
1263 range.position = tPtr->cursorPosition;
1264 range.count = 1;
1265 WMDeleteTextFieldRange(tPtr, range);
1266 data = (void*)WMDeleteTextEvent;
1267 textEvent = WMTextDidChangeNotification;
1270 relay = False;
1272 break;
1274 normal_key:
1275 default:
1276 if (!controled) {
1277 if (count > 0 && !iscntrl(buffer[0])) {
1278 if (tPtr->selection.count)
1279 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1280 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1281 data = (void*)WMInsertTextEvent;
1282 textEvent = WMTextDidChangeNotification;
1284 relay = False;
1287 break;
1290 if (relay) {
1291 WMRelayToNextResponder(W_VIEW(tPtr), event);
1292 return;
1295 /* Do not allow text selection in secure text fields */
1296 if (cancelSelection || tPtr->flags.secure) {
1297 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1299 if (tPtr->selection.count) {
1300 tPtr->selection.count = 0;
1301 refresh = 1;
1303 tPtr->selection.position = tPtr->cursorPosition;
1304 } else {
1305 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1307 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1309 refresh = 1;
1313 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1315 if (textEvent) {
1316 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1318 if (tPtr->delegate) {
1319 if (textEvent==WMTextDidBeginEditingNotification &&
1320 tPtr->delegate->didBeginEditing)
1321 (*tPtr->delegate->didBeginEditing)(tPtr->delegate, notif);
1323 else if (textEvent==WMTextDidEndEditingNotification &&
1324 tPtr->delegate->didEndEditing)
1325 (*tPtr->delegate->didEndEditing)(tPtr->delegate, notif);
1327 else if (textEvent==WMTextDidChangeNotification &&
1328 tPtr->delegate->didChange)
1329 (*tPtr->delegate->didChange)(tPtr->delegate, notif);
1332 WMPostNotification(notif);
1333 WMReleaseNotification(notif);
1336 if (refresh)
1337 paintTextField(tPtr);
1339 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1343 static int
1344 pointToCursorPosition(TextField *tPtr, int x)
1346 int a, b, mid;
1347 int tw;
1349 if (tPtr->flags.bordered)
1350 x -= 2;
1352 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1353 tPtr->textLen - tPtr->viewPosition) < x)
1354 return tPtr->textLen;
1356 a = tPtr->viewPosition;
1357 b = tPtr->textLen;
1359 while (a < b && b-a>1) {
1360 mid = (a+b)/2;
1361 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1362 mid - tPtr->viewPosition);
1363 if (tw > x)
1364 b = mid;
1365 else if (tw < x)
1366 a = mid;
1367 else
1368 return mid;
1371 return (a+b)/2;
1376 static void
1377 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1378 void *cdata, WMData *data)
1380 TextField *tPtr = (TextField*)view->self;
1381 char *str;
1383 tPtr->flags.waitingSelection = 0;
1385 if (data != NULL) {
1386 str = (char*)WMDataBytes(data);
1388 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1389 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1390 (void*)WMInsertTextEvent);
1391 } else {
1392 int n;
1394 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1396 if (str != NULL) {
1397 str[n] = 0;
1398 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1399 XFree(str);
1400 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1401 (void*)WMInsertTextEvent);
1407 static void
1408 handleTextFieldActionEvents(XEvent *event, void *data)
1410 TextField *tPtr = (TextField*)data;
1411 static int move = 0;
1412 static Time lastButtonReleasedEvent = 0;
1413 static Time lastButtonReleasedEvent2 = 0;
1414 Display *dpy = event->xany.display;
1416 CHECK_CLASS(data, WC_TextField);
1418 switch (event->type) {
1419 case KeyPress:
1420 if (tPtr->flags.waitingSelection) {
1421 return;
1423 if (tPtr->flags.enabled && tPtr->flags.focused) {
1424 handleTextFieldKeyPress(tPtr, event);
1425 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1426 W_VIEW(tPtr)->screen->invisibleCursor);
1427 tPtr->flags.pointerGrabbed = 1;
1429 break;
1431 case MotionNotify:
1433 if (tPtr->flags.pointerGrabbed) {
1434 tPtr->flags.pointerGrabbed = 0;
1435 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1436 W_VIEW(tPtr)->screen->textCursor);
1438 if (tPtr->flags.waitingSelection) {
1439 return;
1442 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1444 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1445 tPtr->usableWidth) {
1446 if (WMWidthOfString(tPtr->font,
1447 &(tPtr->text[tPtr->viewPosition]),
1448 tPtr->cursorPosition-tPtr->viewPosition)
1449 > tPtr->usableWidth) {
1450 tPtr->viewPosition++;
1452 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1453 paintCursor(tPtr);
1454 tPtr->viewPosition--;
1457 tPtr->cursorPosition =
1458 pointToCursorPosition(tPtr, event->xmotion.x);
1460 /* Do not allow text selection in secure textfields */
1461 if (tPtr->flags.secure) {
1462 tPtr->selection.position = tPtr->cursorPosition;
1465 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1467 paintCursor(tPtr);
1468 paintTextField(tPtr);
1471 break;
1473 case ButtonPress:
1474 if (tPtr->flags.pointerGrabbed) {
1475 tPtr->flags.pointerGrabbed = 0;
1476 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1477 W_VIEW(tPtr)->screen->textCursor);
1478 break;
1481 if (tPtr->flags.waitingSelection) {
1482 break;
1485 move = 1;
1486 switch (tPtr->flags.alignment) {
1487 int textWidth;
1488 case WARight:
1489 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1490 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1491 WMSetFocusToWidget(tPtr);
1493 if (tPtr->flags.focused) {
1494 tPtr->selection.position = tPtr->cursorPosition;
1495 tPtr->selection.count = 0;
1497 if (textWidth < tPtr->usableWidth) {
1498 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1499 event->xbutton.x - tPtr->usableWidth
1500 + textWidth);
1501 } else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1502 event->xbutton.x);
1504 paintTextField(tPtr);
1505 break;
1507 case WALeft:
1508 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1509 WMSetFocusToWidget(tPtr);
1511 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1512 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1513 event->xbutton.x);
1514 tPtr->selection.position = tPtr->cursorPosition;
1515 tPtr->selection.count = 0;
1516 paintTextField(tPtr);
1518 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1519 char *text;
1520 int n;
1522 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1523 event->xbutton.time,
1524 pasteText, NULL)) {
1525 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1527 if (text) {
1528 text[n] = 0;
1529 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1530 XFree(text);
1531 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1532 (void*)WMInsertTextEvent);
1534 } else {
1535 tPtr->flags.waitingSelection = 1;
1538 break;
1539 default:
1540 break;
1542 break;
1544 case ButtonRelease:
1545 if (tPtr->flags.pointerGrabbed) {
1546 tPtr->flags.pointerGrabbed = 0;
1547 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1548 W_VIEW(tPtr)->screen->textCursor);
1550 if (tPtr->flags.waitingSelection) {
1551 break;
1554 if (!tPtr->flags.secure && tPtr->selection.count!=0) {
1555 int start, count;
1556 XRotateBuffers(dpy, 1);
1558 count = abs(tPtr->selection.count);
1559 if (tPtr->selection.count < 0)
1560 start = tPtr->selection.position - count;
1561 else
1562 start = tPtr->selection.position;
1564 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1567 move = 0;
1569 if (!tPtr->flags.secure &&
1570 event->xbutton.time - lastButtonReleasedEvent
1571 <= WINGsConfiguration.doubleClickDelay) {
1573 if (event->xbutton.time - lastButtonReleasedEvent2 <= 2*WINGsConfiguration.doubleClickDelay) {
1574 tPtr->selection.position = 0;
1575 tPtr->selection.count = tPtr->textLen;
1576 } else {
1577 int pos, cnt;
1578 char *txt;
1579 pos = tPtr->selection.position;
1580 cnt = tPtr->selection.count;
1581 txt = tPtr->text;
1582 while(pos >= 0) {
1583 if (txt[pos] == ' ' || txt[pos] == '\t') break;
1584 pos--;
1586 pos++;
1588 while(pos + cnt < tPtr->textLen) {
1589 if (txt[pos + cnt] == ' ' || txt[pos + cnt] == '\t')
1590 break;
1591 cnt++;
1593 tPtr->selection.position = pos;
1594 tPtr->selection.count = cnt;
1596 paintTextField(tPtr);
1598 if (!tPtr->flags.ownsSelection) {
1599 tPtr->flags.ownsSelection =
1600 WMCreateSelectionHandler(tPtr->view,
1601 XA_PRIMARY,
1602 event->xbutton.time,
1603 &selectionHandler, NULL);
1605 } else if (!tPtr->flags.secure && tPtr->selection.count!=0 &&
1606 !tPtr->flags.ownsSelection) {
1607 tPtr->flags.ownsSelection =
1608 WMCreateSelectionHandler(tPtr->view,
1609 XA_PRIMARY,
1610 event->xbutton.time,
1611 &selectionHandler, NULL);
1614 lastButtonReleasedEvent2 = lastButtonReleasedEvent;
1615 lastButtonReleasedEvent = event->xbutton.time;
1617 break;
1622 static void
1623 destroyTextField(TextField *tPtr)
1625 #if 0
1626 if (tPtr->timerID)
1627 WMDeleteTimerHandler(tPtr->timerID);
1628 #endif
1630 W_DestroyIC(tPtr->view);
1632 WMReleaseFont(tPtr->font);
1633 /*// use lostSelection() instead of WMDeleteSelectionHandler here?*/
1634 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1635 WMRemoveNotificationObserver(tPtr);
1637 if (tPtr->text)
1638 wfree(tPtr->text);
1640 wfree(tPtr);