- Added WMGetTextFieldDelegate()
[wmaker-crm.git] / WINGs / wtextfield.c
blobd576708f73254f1445f678e854174fd8cc536366
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 */
74 /**/
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) {
306 //if (tPtr->flags.ownsSelection)
307 // WMDeleteSelectionHandler(observerView, XA_PRIMARY, CurrentTime);
308 lostSelection(observerView, XA_PRIMARY, NULL);
313 WMTextField*
314 WMCreateTextField(WMWidget *parent)
316 TextField *tPtr;
318 tPtr = wmalloc(sizeof(TextField));
319 memset(tPtr, 0, sizeof(TextField));
321 tPtr->widgetClass = WC_TextField;
323 tPtr->view = W_CreateView(W_VIEW(parent));
324 if (!tPtr->view) {
325 wfree(tPtr);
326 return NULL;
328 tPtr->view->self = tPtr;
330 tPtr->view->delegate = &_TextFieldViewDelegate;
332 tPtr->view->attribFlags |= CWCursor;
333 tPtr->view->attribs.cursor = tPtr->view->screen->textCursor;
335 W_SetViewBackgroundColor(tPtr->view, tPtr->view->screen->white);
337 tPtr->text = wmalloc(MIN_TEXT_BUFFER);
338 tPtr->text[0] = 0;
339 tPtr->textLen = 0;
340 tPtr->bufferSize = MIN_TEXT_BUFFER;
342 tPtr->flags.enabled = 1;
344 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
345 |FocusChangeMask, handleEvents, tPtr);
347 tPtr->font = WMRetainFont(tPtr->view->screen->normalFont);
349 tPtr->flags.bordered = DEFAULT_BORDERED;
350 tPtr->flags.beveled = True;
351 tPtr->flags.alignment = DEFAULT_ALIGNMENT;
352 tPtr->offsetWidth =
353 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
355 W_ResizeView(tPtr->view, DEFAULT_WIDTH, DEFAULT_HEIGHT);
357 WMCreateEventHandler(tPtr->view, EnterWindowMask|LeaveWindowMask
358 |ButtonReleaseMask|ButtonPressMask|KeyPressMask|Button1MotionMask,
359 handleTextFieldActionEvents, tPtr);
361 WMAddNotificationObserver(selectionNotification, tPtr->view,
362 WMSelectionOwnerDidChangeNotification,
363 (void*)XA_PRIMARY);
366 tPtr->flags.cursorOn = 1;
368 return tPtr;
372 void
373 WMSetTextFieldDelegate(WMTextField *tPtr, WMTextFieldDelegate *delegate)
375 CHECK_CLASS(tPtr, WC_TextField);
377 tPtr->delegate = delegate;
381 WMTextFieldDelegate*
382 WMGetTextFieldDelegate(WMTextField *tPtr)
384 CHECK_CLASS(tPtr, WC_TextField);
386 return tPtr->delegate;
390 void
391 WMInsertTextFieldText(WMTextField *tPtr, char *text, int position)
393 int len;
395 CHECK_CLASS(tPtr, WC_TextField);
397 if (!text)
398 return;
400 len = strlen(text);
402 /* check if buffer will hold the text */
403 if (len + tPtr->textLen >= tPtr->bufferSize) {
404 tPtr->bufferSize = tPtr->textLen + len + TEXT_BUFFER_INCR;
405 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
408 if (position < 0 || position >= tPtr->textLen) {
409 /* append the text at the end */
410 strcat(tPtr->text, text);
412 incrToFit(tPtr);
414 tPtr->textLen += len;
415 tPtr->cursorPosition += len;
416 } else {
417 /* insert text at position */
418 memmv(&(tPtr->text[position+len]), &(tPtr->text[position]),
419 tPtr->textLen-position+1);
421 memcpy(&(tPtr->text[position]), text, len);
423 tPtr->textLen += len;
424 if (position >= tPtr->cursorPosition) {
425 tPtr->cursorPosition += len;
426 incrToFit2(tPtr);
427 } else {
428 incrToFit(tPtr);
432 paintTextField(tPtr);
435 void
436 WMDeleteTextFieldRange(WMTextField *tPtr, WMRange range)
438 CHECK_CLASS(tPtr, WC_TextField);
440 normalizeRange(tPtr, &range);
442 if (!range.count)
443 return;
445 memmv(&(tPtr->text[range.position]), &(tPtr->text[range.position+range.count]),
446 tPtr->textLen - (range.position+range.count) + 1);
448 tPtr->textLen -= range.count;
450 /* try to keep cursorPosition at the same place */
451 tPtr->viewPosition -= range.count;
452 if (tPtr->viewPosition < 0)
453 tPtr->viewPosition = 0;
454 tPtr->cursorPosition = range.position;
456 decrToFit(tPtr);
458 paintTextField(tPtr);
463 char*
464 WMGetTextFieldText(WMTextField *tPtr)
466 CHECK_CLASS(tPtr, WC_TextField);
468 return wstrdup(tPtr->text);
472 void
473 WMSetTextFieldText(WMTextField *tPtr, char *text)
475 CHECK_CLASS(tPtr, WC_TextField);
477 if ((text && strcmp(tPtr->text, text) == 0) ||
478 (!text && tPtr->textLen == 0))
479 return;
481 if (text==NULL) {
482 tPtr->text[0] = 0;
483 tPtr->textLen = 0;
484 } else {
485 tPtr->textLen = strlen(text);
487 if (tPtr->textLen >= tPtr->bufferSize) {
488 tPtr->bufferSize = tPtr->textLen + TEXT_BUFFER_INCR;
489 tPtr->text = wrealloc(tPtr->text, tPtr->bufferSize);
491 strcpy(tPtr->text, text);
494 tPtr->cursorPosition = tPtr->selection.position = tPtr->textLen;
495 tPtr->viewPosition = 0;
496 tPtr->selection.count = 0;
498 if (tPtr->view->flags.realized)
499 paintTextField(tPtr);
503 void
504 WMSetTextFieldAlignment(WMTextField *tPtr, WMAlignment alignment)
506 CHECK_CLASS(tPtr, WC_TextField);
508 tPtr->flags.alignment = alignment;
510 if (alignment!=WALeft) {
511 wwarning("only left alignment is supported in textfields");
512 return;
515 if (tPtr->view->flags.realized) {
516 paintTextField(tPtr);
521 void
522 WMSetTextFieldBordered(WMTextField *tPtr, Bool bordered)
524 CHECK_CLASS(tPtr, WC_TextField);
526 tPtr->flags.bordered = bordered;
528 if (tPtr->view->flags.realized) {
529 paintTextField(tPtr);
534 void
535 WMSetTextFieldBeveled(WMTextField *tPtr, Bool flag)
537 CHECK_CLASS(tPtr, WC_TextField);
539 tPtr->flags.beveled = ((flag==0) ? 0 : 1);
541 if (tPtr->view->flags.realized) {
542 paintTextField(tPtr);
548 void
549 WMSetTextFieldSecure(WMTextField *tPtr, Bool flag)
551 CHECK_CLASS(tPtr, WC_TextField);
553 tPtr->flags.secure = ((flag==0) ? 0 : 1);
555 if (tPtr->view->flags.realized) {
556 paintTextField(tPtr);
561 Bool
562 WMGetTextFieldEditable(WMTextField *tPtr)
564 CHECK_CLASS(tPtr, WC_TextField);
566 return tPtr->flags.enabled;
570 void
571 WMSetTextFieldEditable(WMTextField *tPtr, Bool flag)
573 CHECK_CLASS(tPtr, WC_TextField);
575 tPtr->flags.enabled = ((flag==0) ? 0 : 1);
577 if (tPtr->view->flags.realized) {
578 paintTextField(tPtr);
583 void
584 WMSelectTextFieldRange(WMTextField *tPtr, WMRange range)
586 CHECK_CLASS(tPtr, WC_TextField);
588 if (tPtr->flags.enabled) {
589 normalizeRange(tPtr, &range);
591 tPtr->selection = range;
593 tPtr->cursorPosition = range.position + range.count;
595 if (tPtr->view->flags.realized) {
596 paintTextField(tPtr);
602 void
603 WMSetTextFieldCursorPosition(WMTextField *tPtr, unsigned int position)
605 CHECK_CLASS(tPtr, WC_TextField);
607 if (tPtr->flags.enabled) {
608 if (position > tPtr->textLen)
609 position = tPtr->textLen;
611 tPtr->cursorPosition = position;
612 if (tPtr->view->flags.realized) {
613 paintTextField(tPtr);
619 void
620 WMSetTextFieldNextTextField(WMTextField *tPtr, WMTextField *next)
622 CHECK_CLASS(tPtr, WC_TextField);
623 if (next == NULL) {
624 if (tPtr->view->nextFocusChain)
625 tPtr->view->nextFocusChain->prevFocusChain = NULL;
626 tPtr->view->nextFocusChain = NULL;
627 return;
630 CHECK_CLASS(next, WC_TextField);
632 if (tPtr->view->nextFocusChain)
633 tPtr->view->nextFocusChain->prevFocusChain = NULL;
634 if (next->view->prevFocusChain)
635 next->view->prevFocusChain->nextFocusChain = NULL;
637 tPtr->view->nextFocusChain = next->view;
638 next->view->prevFocusChain = tPtr->view;
642 void
643 WMSetTextFieldPrevTextField(WMTextField *tPtr, WMTextField *prev)
645 CHECK_CLASS(tPtr, WC_TextField);
646 if (prev == NULL) {
647 if (tPtr->view->prevFocusChain)
648 tPtr->view->prevFocusChain->nextFocusChain = NULL;
649 tPtr->view->prevFocusChain = NULL;
650 return;
653 CHECK_CLASS(prev, WC_TextField);
655 if (tPtr->view->prevFocusChain)
656 tPtr->view->prevFocusChain->nextFocusChain = NULL;
657 if (prev->view->nextFocusChain)
658 prev->view->nextFocusChain->prevFocusChain = NULL;
660 tPtr->view->prevFocusChain = prev->view;
661 prev->view->nextFocusChain = tPtr->view;
665 void
666 WMSetTextFieldFont(WMTextField *tPtr, WMFont *font)
668 CHECK_CLASS(tPtr, WC_TextField);
670 if (tPtr->font)
671 WMReleaseFont(tPtr->font);
672 tPtr->font = WMRetainFont(font);
674 tPtr->offsetWidth =
675 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
677 if (tPtr->view->flags.realized) {
678 paintTextField(tPtr);
684 WMFont*
685 WMGetTextFieldFont(WMTextField *tPtr)
687 return tPtr->font;
691 static void
692 didResizeTextField(W_ViewDelegate *self, WMView *view)
694 WMTextField *tPtr = (WMTextField*)view->self;
696 tPtr->offsetWidth =
697 WMAX((tPtr->view->size.height - WMFontHeight(tPtr->font))/2, 1);
699 tPtr->usableWidth = tPtr->view->size.width - 2*tPtr->offsetWidth /*+ 2*/;
703 static char*
704 makeHiddenString(int length)
706 char *data = wmalloc(length+1);
708 memset(data, '*', length);
709 data[length] = '\0';
711 return data;
715 static void
716 paintCursor(TextField *tPtr)
718 int cx;
719 WMScreen *screen = tPtr->view->screen;
720 int textWidth;
721 char *text;
723 if (tPtr->flags.secure)
724 text = makeHiddenString(strlen(tPtr->text));
725 else
726 text = tPtr->text;
728 cx = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
729 tPtr->cursorPosition-tPtr->viewPosition);
731 switch (tPtr->flags.alignment) {
732 case WARight:
733 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
734 if (textWidth < tPtr->usableWidth)
735 cx += tPtr->offsetWidth + tPtr->usableWidth - textWidth + 1;
736 else
737 cx += tPtr->offsetWidth + 1;
738 break;
739 case WALeft:
740 cx += tPtr->offsetWidth + 1;
741 break;
742 case WAJustified:
743 /* not supported */
744 case WACenter:
745 textWidth = WMWidthOfString(tPtr->font, text, tPtr->textLen);
746 if (textWidth < tPtr->usableWidth)
747 cx += tPtr->offsetWidth + (tPtr->usableWidth-textWidth)/2;
748 else
749 cx += tPtr->offsetWidth;
750 break;
753 XDrawRectangle(screen->display, tPtr->view->window, screen->xorGC,
754 cx, tPtr->offsetWidth, 1,
755 tPtr->view->size.height - 2*tPtr->offsetWidth - 1);
756 printf("%d %d\n",cx,tPtr->cursorPosition);
758 XDrawLine(screen->display, tPtr->view->window, screen->xorGC,
759 cx, tPtr->offsetWidth, cx,
760 tPtr->view->size.height - tPtr->offsetWidth - 1);
762 if (tPtr->flags.secure)
763 wfree(text);
768 static void
769 drawRelief(WMView *view, Bool beveled)
771 WMScreen *scr = view->screen;
772 Display *dpy = scr->display;
773 GC wgc;
774 GC lgc;
775 GC dgc;
776 int width = view->size.width;
777 int height = view->size.height;
779 dgc = WMColorGC(scr->darkGray);
781 if (!beveled) {
782 XDrawRectangle(dpy, view->window, dgc, 0, 0, width-1, height-1);
784 return;
786 wgc = WMColorGC(scr->white);
787 lgc = WMColorGC(scr->gray);
789 /* top left */
790 XDrawLine(dpy, view->window, dgc, 0, 0, width-1, 0);
791 XDrawLine(dpy, view->window, dgc, 0, 1, width-2, 1);
793 XDrawLine(dpy, view->window, dgc, 0, 0, 0, height-2);
794 XDrawLine(dpy, view->window, dgc, 1, 0, 1, height-3);
796 /* bottom right */
797 XDrawLine(dpy, view->window, wgc, 0, height-1, width-1, height-1);
798 XDrawLine(dpy, view->window, lgc, 1, height-2, width-2, height-2);
800 XDrawLine(dpy, view->window, wgc, width-1, 0, width-1, height-1);
801 XDrawLine(dpy, view->window, lgc, width-2, 1, width-2, height-3);
805 static void
806 paintTextField(TextField *tPtr)
808 W_Screen *screen = tPtr->view->screen;
809 W_View *view = tPtr->view;
810 W_View viewbuffer;
811 int tx, ty, tw, th;
812 int rx;
813 int bd;
814 int totalWidth;
815 char *text;
816 Pixmap drawbuffer;
819 if (!view->flags.realized || !view->flags.mapped)
820 return;
822 if (!tPtr->flags.bordered) {
823 bd = 0;
824 } else {
825 bd = 2;
828 if (tPtr->flags.secure) {
829 text = makeHiddenString(strlen(tPtr->text));
830 } else {
831 text = tPtr->text;
834 totalWidth = tPtr->view->size.width - 2*bd;
836 drawbuffer = XCreatePixmap(screen->display, view->window,
837 view->size.width, view->size.height, screen->depth);
838 XFillRectangle(screen->display, drawbuffer, WMColorGC(screen->white),
839 0,0, view->size.width,view->size.height);
840 /* this is quite dirty */
841 viewbuffer.screen = view->screen;
842 viewbuffer.size = view->size;
843 viewbuffer.window = drawbuffer;
846 if (tPtr->textLen > 0) {
847 tw = WMWidthOfString(tPtr->font, &(text[tPtr->viewPosition]),
848 tPtr->textLen - tPtr->viewPosition);
850 th = WMFontHeight(tPtr->font);
852 ty = tPtr->offsetWidth;
853 switch (tPtr->flags.alignment) {
854 case WALeft:
855 tx = tPtr->offsetWidth + 1;
856 if (tw < tPtr->usableWidth)
857 XFillRectangle(screen->display, drawbuffer,
858 WMColorGC(screen->white),
859 bd+tw,bd, totalWidth-tw,view->size.height-2*bd);
860 break;
862 case WACenter:
863 tx = tPtr->offsetWidth + (tPtr->usableWidth - tw) / 2;
864 if (tw < tPtr->usableWidth)
865 XClearArea(screen->display, view->window, bd, bd,
866 totalWidth, view->size.height-2*bd, False);
867 break;
869 default:
870 case WARight:
871 tx = tPtr->offsetWidth + tPtr->usableWidth - tw - 1;
872 if (tw < tPtr->usableWidth)
873 XClearArea(screen->display, view->window, bd, bd,
874 totalWidth-tw, view->size.height-2*bd, False);
875 break;
878 if (!tPtr->flags.enabled)
879 WMSetColorInGC(screen->darkGray, screen->textFieldGC);
881 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
882 tPtr->font, tx, ty,
883 &(text[tPtr->viewPosition]),
884 tPtr->textLen - tPtr->viewPosition);
886 if (tPtr->selection.count) {
887 int count,count2;
889 count = tPtr->selection.count < 0
890 ? tPtr->selection.position + tPtr->selection.count
891 : tPtr->selection.position;
892 count2 = abs(tPtr->selection.count);
893 if (count < tPtr->viewPosition) {
894 count2 = abs(count2 - abs(tPtr->viewPosition - count));
895 count = tPtr->viewPosition;
899 rx = tPtr->offsetWidth + 1 + WMWidthOfString(tPtr->font,text,count)
900 - WMWidthOfString(tPtr->font,text,tPtr->viewPosition);
902 XSetBackground(screen->display, screen->textFieldGC,
903 screen->gray->color.pixel);
905 WMDrawImageString(screen, drawbuffer, screen->textFieldGC,
906 tPtr->font, rx, ty, &(text[count]),
907 count2);
909 XSetBackground(screen->display, screen->textFieldGC,
910 screen->white->color.pixel);
913 if (!tPtr->flags.enabled)
914 WMSetColorInGC(screen->black, screen->textFieldGC);
915 } else {
916 XFillRectangle(screen->display, drawbuffer,
917 WMColorGC(screen->white),
918 bd,bd, totalWidth,view->size.height-2*bd);
921 /* draw relief */
922 if (tPtr->flags.bordered) {
923 drawRelief(&viewbuffer, tPtr->flags.beveled);
926 if (tPtr->flags.secure)
927 wfree(text);
928 XCopyArea(screen->display, drawbuffer, view->window,
929 screen->copyGC, 0,0, view->size.width,
930 view->size.height,0,0);
931 XFreePixmap(screen->display, drawbuffer);
933 /* draw cursor */
934 if (tPtr->flags.focused && tPtr->flags.enabled && tPtr->flags.cursorOn) {
935 paintCursor(tPtr);
940 #if 0
941 static void
942 blinkCursor(void *data)
944 TextField *tPtr = (TextField*)data;
946 if (tPtr->flags.cursorOn) {
947 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY, blinkCursor,
948 data);
949 } else {
950 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY, blinkCursor,
951 data);
953 paintCursor(tPtr);
954 tPtr->flags.cursorOn = !tPtr->flags.cursorOn;
956 #endif
959 static void
960 handleEvents(XEvent *event, void *data)
962 TextField *tPtr = (TextField*)data;
964 CHECK_CLASS(data, WC_TextField);
967 switch (event->type) {
968 case FocusIn:
969 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))!=tPtr->view)
970 return;
971 tPtr->flags.focused = 1;
972 #if 0
973 if (!tPtr->timerID) {
974 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
975 blinkCursor, tPtr);
977 #endif
978 paintTextField(tPtr);
980 NOTIFY(tPtr, didBeginEditing, WMTextDidBeginEditingNotification, NULL);
982 tPtr->flags.notIllegalMovement = 0;
983 break;
985 case FocusOut:
986 tPtr->flags.focused = 0;
987 #if 0
988 if (tPtr->timerID)
989 WMDeleteTimerHandler(tPtr->timerID);
990 tPtr->timerID = NULL;
991 #endif
993 paintTextField(tPtr);
994 if (!tPtr->flags.notIllegalMovement) {
995 NOTIFY(tPtr, didEndEditing, WMTextDidEndEditingNotification,
996 (void*)WMIllegalTextMovement);
998 break;
1000 case Expose:
1001 if (event->xexpose.count!=0)
1002 break;
1003 paintTextField(tPtr);
1004 break;
1006 case DestroyNotify:
1007 destroyTextField(tPtr);
1008 break;
1013 static void
1014 handleTextFieldKeyPress(TextField *tPtr, XEvent *event)
1016 char buffer[64];
1017 KeySym ksym;
1018 char *textEvent = NULL;
1019 void *data = NULL;
1020 int count, refresh = 0;
1021 int control_pressed = 0;
1022 int cancelSelection = 1;
1023 Bool shifted, controled, modified;
1024 Bool relay = True;
1026 /*printf("(%d,%d) -> ", tPtr->selection.position, tPtr->selection.count);*/
1027 if (((XKeyEvent *) event)->state & WM_EMACSKEYMASK)
1028 control_pressed = 1;
1030 shifted = (event->xkey.state & ShiftMask ? True : False);
1031 controled = (event->xkey.state & ControlMask ? True : False);
1032 modified = shifted || controled;
1034 count = XLookupString(&event->xkey, buffer, 63, &ksym, NULL);
1035 buffer[count] = '\0';
1037 switch (ksym) {
1038 case XK_Tab:
1039 #ifdef XK_ISO_Left_Tab
1040 case XK_ISO_Left_Tab:
1041 #endif
1042 if (!controled) {
1043 if (shifted) {
1044 if (tPtr->view->prevFocusChain) {
1045 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
1046 tPtr->view->prevFocusChain);
1047 tPtr->flags.notIllegalMovement = 1;
1049 data = (void*)WMBacktabTextMovement;
1050 } else {
1051 if (tPtr->view->nextFocusChain) {
1052 W_SetFocusOfTopLevel(W_TopLevelOfView(tPtr->view),
1053 tPtr->view->nextFocusChain);
1054 tPtr->flags.notIllegalMovement = 1;
1056 data = (void*)WMTabTextMovement;
1058 textEvent = WMTextDidEndEditingNotification;
1060 cancelSelection = 0;
1062 relay = False;
1064 break;
1066 case XK_Escape:
1067 if (!modified) {
1068 data = (void*)WMEscapeTextMovement;
1069 textEvent = WMTextDidEndEditingNotification;
1071 relay = False;
1073 break;
1075 case XK_Return:
1076 if (!modified) {
1077 data = (void*)WMReturnTextMovement;
1078 textEvent = WMTextDidEndEditingNotification;
1080 relay = False;
1082 break;
1084 case WM_EMACSKEY_LEFT:
1085 if (!control_pressed)
1086 goto normal_key;
1087 else
1088 controled = False;
1090 #ifdef XK_KP_Left
1091 case XK_KP_Left:
1092 #endif
1093 case XK_Left:
1094 if (tPtr->cursorPosition > 0) {
1095 paintCursor(tPtr);
1096 if (controled) {
1097 int i = tPtr->cursorPosition - 1;
1099 while (i > 0 && tPtr->text[i] != ' ') i--;
1100 while (i > 0 && tPtr->text[i] == ' ') i--;
1102 tPtr->cursorPosition = (i > 0) ? i + 1 : 0;
1103 } else
1104 tPtr->cursorPosition--;
1106 if (tPtr->cursorPosition < tPtr->viewPosition) {
1107 tPtr->viewPosition = tPtr->cursorPosition;
1108 refresh = 1;
1109 } else
1110 paintCursor(tPtr);
1112 if (shifted)
1113 cancelSelection = 0;
1115 relay = False;
1117 break;
1119 case WM_EMACSKEY_RIGHT:
1120 if (!control_pressed)
1121 goto normal_key;
1122 else
1123 controled = False;
1125 #ifdef XK_KP_Right
1126 case XK_KP_Right:
1127 #endif
1128 case XK_Right:
1129 if (tPtr->cursorPosition < tPtr->textLen) {
1130 paintCursor(tPtr);
1131 if (controled) {
1132 int i = tPtr->cursorPosition;
1134 while (tPtr->text[i] && tPtr->text[i] != ' ') i++;
1135 while (tPtr->text[i] == ' ') i++;
1137 tPtr->cursorPosition = i;
1138 } else {
1139 tPtr->cursorPosition++;
1141 while (WMWidthOfString(tPtr->font,
1142 &(tPtr->text[tPtr->viewPosition]),
1143 tPtr->cursorPosition-tPtr->viewPosition)
1144 > tPtr->usableWidth) {
1145 tPtr->viewPosition++;
1146 refresh = 1;
1148 if (!refresh)
1149 paintCursor(tPtr);
1151 if (shifted)
1152 cancelSelection = 0;
1154 relay = False;
1156 break;
1158 case WM_EMACSKEY_HOME:
1159 if (!control_pressed)
1160 goto normal_key;
1161 else
1162 controled = False;
1164 #ifdef XK_KP_Home
1165 case XK_KP_Home:
1166 #endif
1167 case XK_Home:
1168 if (!controled) {
1169 if (tPtr->cursorPosition > 0) {
1170 paintCursor(tPtr);
1171 tPtr->cursorPosition = 0;
1172 if (tPtr->viewPosition > 0) {
1173 tPtr->viewPosition = 0;
1174 refresh = 1;
1175 } else
1176 paintCursor(tPtr);
1178 if (shifted)
1179 cancelSelection = 0;
1181 relay = False;
1183 break;
1185 case WM_EMACSKEY_END:
1186 if (!control_pressed)
1187 goto normal_key;
1188 else
1189 controled = False;
1191 #ifdef XK_KP_End
1192 case XK_KP_End:
1193 #endif
1194 case XK_End:
1195 if (!controled) {
1196 if (tPtr->cursorPosition < tPtr->textLen) {
1197 paintCursor(tPtr);
1198 tPtr->cursorPosition = tPtr->textLen;
1199 tPtr->viewPosition = 0;
1200 while (WMWidthOfString(tPtr->font,
1201 &(tPtr->text[tPtr->viewPosition]),
1202 tPtr->textLen-tPtr->viewPosition)
1203 > tPtr->usableWidth) {
1204 tPtr->viewPosition++;
1205 refresh = 1;
1207 if (!refresh)
1208 paintCursor(tPtr);
1210 if (shifted)
1211 cancelSelection = 0;
1213 relay = False;
1215 break;
1217 case WM_EMACSKEY_BS:
1218 if (!control_pressed)
1219 goto normal_key;
1220 else
1221 modified = False;
1223 case XK_BackSpace:
1224 if (!modified) {
1225 if (tPtr->selection.count) {
1226 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1227 data = (void*)WMDeleteTextEvent;
1228 textEvent = WMTextDidChangeNotification;
1229 } else if (tPtr->cursorPosition > 0) {
1230 WMRange range;
1231 range.position = tPtr->cursorPosition - 1;
1232 range.count = 1;
1233 WMDeleteTextFieldRange(tPtr, range);
1234 data = (void*)WMDeleteTextEvent;
1235 textEvent = WMTextDidChangeNotification;
1238 relay = False;
1240 break;
1242 case WM_EMACSKEY_DEL:
1243 if (!control_pressed)
1244 goto normal_key;
1245 else
1246 modified = False;
1248 #ifdef XK_KP_Delete
1249 case XK_KP_Delete:
1250 #endif
1251 case XK_Delete:
1252 if (!modified) {
1253 if (tPtr->selection.count) {
1254 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1255 data = (void*)WMDeleteTextEvent;
1256 textEvent = WMTextDidChangeNotification;
1257 } else if (tPtr->cursorPosition < tPtr->textLen) {
1258 WMRange range;
1259 range.position = tPtr->cursorPosition;
1260 range.count = 1;
1261 WMDeleteTextFieldRange(tPtr, range);
1262 data = (void*)WMDeleteTextEvent;
1263 textEvent = WMTextDidChangeNotification;
1266 relay = False;
1268 break;
1270 normal_key:
1271 default:
1272 if (!controled) {
1273 if (count > 0 && !iscntrl(buffer[0])) {
1274 if (tPtr->selection.count)
1275 WMDeleteTextFieldRange(tPtr, tPtr->selection);
1276 WMInsertTextFieldText(tPtr, buffer, tPtr->cursorPosition);
1277 data = (void*)WMInsertTextEvent;
1278 textEvent = WMTextDidChangeNotification;
1280 relay = False;
1283 break;
1286 if (relay) {
1287 WMRelayToNextResponder(W_VIEW(tPtr), event);
1288 return;
1291 /* Do not allow text selection in secure text fields */
1292 if (cancelSelection || tPtr->flags.secure) {
1293 lostSelection(tPtr->view, XA_PRIMARY, NULL);
1295 if (tPtr->selection.count) {
1296 tPtr->selection.count = 0;
1297 refresh = 1;
1299 tPtr->selection.position = tPtr->cursorPosition;
1300 } else {
1301 if (tPtr->selection.count != tPtr->cursorPosition - tPtr->selection.position) {
1303 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1305 refresh = 1;
1309 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1311 if (textEvent) {
1312 WMNotification *notif = WMCreateNotification(textEvent, tPtr, data);
1314 if (tPtr->delegate) {
1315 if (textEvent==WMTextDidBeginEditingNotification &&
1316 tPtr->delegate->didBeginEditing)
1317 (*tPtr->delegate->didBeginEditing)(tPtr->delegate, notif);
1319 else if (textEvent==WMTextDidEndEditingNotification &&
1320 tPtr->delegate->didEndEditing)
1321 (*tPtr->delegate->didEndEditing)(tPtr->delegate, notif);
1323 else if (textEvent==WMTextDidChangeNotification &&
1324 tPtr->delegate->didChange)
1325 (*tPtr->delegate->didChange)(tPtr->delegate, notif);
1328 WMPostNotification(notif);
1329 WMReleaseNotification(notif);
1332 if (refresh)
1333 paintTextField(tPtr);
1335 /*printf("(%d,%d)\n", tPtr->selection.position, tPtr->selection.count);*/
1339 static int
1340 pointToCursorPosition(TextField *tPtr, int x)
1342 int a, b, mid;
1343 int tw;
1345 if (tPtr->flags.bordered)
1346 x -= 2;
1348 a = tPtr->viewPosition;
1349 b = tPtr->viewPosition + tPtr->textLen;
1350 if (WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1351 tPtr->textLen - tPtr->viewPosition) < x)
1352 return tPtr->textLen;
1354 while (a < b && b-a>1) {
1355 mid = (a+b)/2;
1356 tw = WMWidthOfString(tPtr->font, &(tPtr->text[tPtr->viewPosition]),
1357 mid - tPtr->viewPosition);
1358 if (tw > x)
1359 b = mid;
1360 else if (tw < x)
1361 a = mid;
1362 else
1363 return mid;
1365 return (a+b)/2;
1370 static void
1371 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
1372 void *cdata, WMData *data)
1374 TextField *tPtr = (TextField*)view->self;
1375 char *str;
1377 tPtr->flags.waitingSelection = 0;
1379 if (data != NULL) {
1380 str = (char*)WMDataBytes(data);
1382 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1383 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1384 (void*)WMInsertTextEvent);
1385 } else {
1386 int n;
1388 str = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1390 if (str != NULL) {
1391 str[n] = 0;
1392 WMInsertTextFieldText(tPtr, str, tPtr->cursorPosition);
1393 XFree(str);
1394 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1395 (void*)WMInsertTextEvent);
1401 static void
1402 handleTextFieldActionEvents(XEvent *event, void *data)
1404 TextField *tPtr = (TextField*)data;
1405 static int move = 0;
1406 static Time lastButtonReleasedEvent = 0;
1407 Display *dpy = event->xany.display;
1409 CHECK_CLASS(data, WC_TextField);
1411 switch (event->type) {
1412 case KeyPress:
1413 if (tPtr->flags.waitingSelection) {
1414 return;
1416 if (tPtr->flags.enabled && tPtr->flags.focused) {
1417 handleTextFieldKeyPress(tPtr, event);
1418 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1419 W_VIEW(tPtr)->screen->invisibleCursor);
1420 tPtr->flags.pointerGrabbed = 1;
1422 break;
1424 case MotionNotify:
1426 if (tPtr->flags.pointerGrabbed) {
1427 tPtr->flags.pointerGrabbed = 0;
1428 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1429 W_VIEW(tPtr)->screen->textCursor);
1431 if (tPtr->flags.waitingSelection) {
1432 return;
1435 if (tPtr->flags.enabled && (event->xmotion.state & Button1Mask)) {
1437 if (tPtr->viewPosition < tPtr->textLen && event->xmotion.x >
1438 tPtr->usableWidth) {
1439 if (WMWidthOfString(tPtr->font,
1440 &(tPtr->text[tPtr->viewPosition]),
1441 tPtr->cursorPosition-tPtr->viewPosition)
1442 > tPtr->usableWidth) {
1443 tPtr->viewPosition++;
1445 } else if (tPtr->viewPosition > 0 && event->xmotion.x < 0) {
1446 paintCursor(tPtr);
1447 tPtr->viewPosition--;
1450 tPtr->cursorPosition =
1451 pointToCursorPosition(tPtr, event->xmotion.x);
1453 /* Do not allow text selection in secure textfields */
1454 if (tPtr->flags.secure) {
1455 tPtr->selection.position = tPtr->cursorPosition;
1458 tPtr->selection.count = tPtr->cursorPosition - tPtr->selection.position;
1460 paintCursor(tPtr);
1461 paintTextField(tPtr);
1464 break;
1466 case ButtonPress:
1467 if (tPtr->flags.pointerGrabbed) {
1468 tPtr->flags.pointerGrabbed = 0;
1469 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1470 W_VIEW(tPtr)->screen->textCursor);
1471 break;
1474 if (tPtr->flags.waitingSelection) {
1475 break;
1478 move = 1;
1479 switch (tPtr->flags.alignment) {
1480 int textWidth;
1481 case WARight:
1482 textWidth = WMWidthOfString(tPtr->font, tPtr->text, tPtr->textLen);
1483 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1484 WMSetFocusToWidget(tPtr);
1486 if (tPtr->flags.focused) {
1487 tPtr->selection.position = tPtr->cursorPosition;
1488 tPtr->selection.count = 0;
1490 if (textWidth < tPtr->usableWidth) {
1491 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1492 event->xbutton.x - tPtr->usableWidth
1493 + textWidth);
1494 } else tPtr->cursorPosition = pointToCursorPosition(tPtr,
1495 event->xbutton.x);
1497 paintTextField(tPtr);
1498 break;
1500 case WALeft:
1501 if (tPtr->flags.enabled && !tPtr->flags.focused) {
1502 WMSetFocusToWidget(tPtr);
1504 if (tPtr->flags.focused && event->xbutton.button == Button1) {
1505 tPtr->cursorPosition = pointToCursorPosition(tPtr,
1506 event->xbutton.x);
1507 tPtr->selection.position = tPtr->cursorPosition;
1508 tPtr->selection.count = 0;
1509 paintTextField(tPtr);
1511 if (event->xbutton.button == Button2 && tPtr->flags.enabled) {
1512 char *text;
1513 int n;
1515 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
1516 event->xbutton.time,
1517 pasteText, NULL)) {
1518 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
1520 if (text) {
1521 text[n] = 0;
1522 WMInsertTextFieldText(tPtr, text, tPtr->cursorPosition);
1523 XFree(text);
1524 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
1525 (void*)WMInsertTextEvent);
1527 } else {
1528 tPtr->flags.waitingSelection = 1;
1531 break;
1532 default:
1533 break;
1535 break;
1537 case ButtonRelease:
1538 if (tPtr->flags.pointerGrabbed) {
1539 tPtr->flags.pointerGrabbed = 0;
1540 XDefineCursor(dpy, W_VIEW(tPtr)->window,
1541 W_VIEW(tPtr)->screen->textCursor);
1543 if (tPtr->flags.waitingSelection) {
1544 break;
1547 if (!tPtr->flags.secure && tPtr->selection.count!=0) {
1548 int start, count;
1549 XRotateBuffers(dpy, 1);
1551 count = abs(tPtr->selection.count);
1552 if (tPtr->selection.count < 0)
1553 start = tPtr->selection.position - count;
1554 else
1555 start = tPtr->selection.position;
1557 XStoreBuffer(dpy, &tPtr->text[start], count, 0);
1560 move = 0;
1562 if (!tPtr->flags.secure &&
1563 event->xbutton.time - lastButtonReleasedEvent
1564 <= WINGsConfiguration.doubleClickDelay) {
1565 tPtr->selection.position = 0;
1566 tPtr->selection.count = tPtr->textLen;
1567 paintTextField(tPtr);
1569 if (!tPtr->flags.ownsSelection) {
1570 tPtr->flags.ownsSelection =
1571 WMCreateSelectionHandler(tPtr->view,
1572 XA_PRIMARY,
1573 event->xbutton.time,
1574 &selectionHandler, NULL);
1576 } else if (!tPtr->flags.secure && tPtr->selection.count!=0 &&
1577 !tPtr->flags.ownsSelection) {
1578 tPtr->flags.ownsSelection =
1579 WMCreateSelectionHandler(tPtr->view,
1580 XA_PRIMARY,
1581 event->xbutton.time,
1582 &selectionHandler, NULL);
1585 lastButtonReleasedEvent = event->xbutton.time;
1587 break;
1592 static void
1593 destroyTextField(TextField *tPtr)
1595 #if 0
1596 if (tPtr->timerID)
1597 WMDeleteTimerHandler(tPtr->timerID);
1598 #endif
1600 WMReleaseFont(tPtr->font);
1601 // use lostSelection() instead of WMDeleteSelectionHandler aici?
1602 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
1603 WMRemoveNotificationObserver(tPtr);
1605 if (tPtr->text)
1606 wfree(tPtr->text);
1608 wfree(tPtr);