more cursor motion and text entry fixes, esp. for graphic blocks...
[wmaker-crm.git] / WINGs / wtext.c
blob12865f298b6b01d7c93181ba4cc35d375b14bf49
2 /* WINGs WMText: multi-line/font/color/graphic text widget, by Nwanua. */
5 #include "WINGsP.h"
6 #include <ctype.h>
7 #include <X11/keysym.h>
8 #include <X11/Xatom.h>
10 #define DO_BLINK 0
12 /* TODO:
13 * - verify what happens with XK_return in insertTextInt...
14 * - selection code... selects can be funny if it crosses over. use rect?
15 * - also inspect behaviour for WACenter and WARight
16 * - what if a widget grabs the click... howto say: "pressed me"?
17 * note that WMCreateEventHandler takes one data, but need widget & tPtr
18 * - FIX: graphix blocks MUST be skipped if monoFont even though they exist!
19 * - check if support for Horizontal Scroll is complete
20 * - assess danger of destroying widgets whose actions link to other pages
21 * - Tabs now are simply replaced by 4 spaces...
22 * - redo blink code to reduce paint event... use pixmap buffer...
23 * - add paragraph support (full) and '\n' code in getStream..
27 /* a Section is a section of a TextBlock that describes what parts
28 of a TextBlock has been laid out on which "line"...
29 o this greatly aids redraw, scroll and selection.
30 o this is created during layoutLine, but may be later modified.
31 o there may be many Sections per TextBlock, hence the array */
32 typedef struct {
33 unsigned int x, y; /* where to draw it from */
34 unsigned short w, h; /* its width and height */
35 unsigned short begin; /* where the layout begins */
36 unsigned short end ; /* where it ends */
37 unsigned short max_d; /* a quick hack for layOut if(laidOut) */
38 unsigned short last:1; /* is it the last section on a "line"? */
39 unsigned int _y:31; /* the "line" it and other textblocks are on */
40 } Section;
43 /* a TextBlock is a node in a doubly-linked list of TextBlocks containing:
44 o text for the block, color and font
45 o or a pointer to the pixmap
46 o OR a pointer to the widget and the (text) description for its graphic
49 typedef struct _TextBlock {
50 struct _TextBlock *next; /* next text block in linked list */
51 struct _TextBlock *prior; /* prior text block in linked list */
53 char *text; /* pointer to text (could be kanji) */
54 /* or to the object's description */
55 union {
56 WMFont *font; /* the font */
57 WMWidget *widget; /* the embedded widget */
58 WMPixmap *pixmap; /* the pixmap */
59 } d; /* description */
61 unsigned short used; /* number of chars in this block */
62 unsigned short allocated; /* size of allocation (in chars) */
63 WMColor *color; /* the color */
65 Section *sections; /* the region for layouts (a growable array) */
66 /* an _array_! of size _nsections_ */
68 unsigned short s_begin; /* where the selection begins */
69 unsigned short s_end; /* where it ends */
71 unsigned int first:1; /* first TextBlock in paragraph */
72 unsigned int blank:1; /* ie. blank paragraph */
73 unsigned int kanji:1; /* is of 16-bit characters or not */
74 unsigned int graphic:1; /* graphic or text: text=0 */
75 unsigned int object:1; /* embedded object or pixmap */
76 unsigned int underlined:1; /* underlined or not */
77 unsigned int selected:1; /* selected or not */
78 unsigned int nsections:8; /* over how many "lines" a TextBlock wraps */
79 int script:8; /* script in points: negative for subscript */
80 unsigned int marginN:8; /* which of the margins in the tPtr to use */
81 unsigned int nClicks:2; /* single, double, triple clicks */
82 unsigned int RESERVED:7;
83 } TextBlock;
86 /* I'm lazy: visible.h vs. visible.size.height :-) */
87 typedef struct {
88 int y, x, h, w;
89 } myRect;
92 typedef struct W_Text {
93 W_Class widgetClass; /* the class number of this widget */
94 W_View *view; /* the view referring to this instance */
96 WMRuler *ruler; /* the ruler widget to manipulate paragraphs */
98 WMScroller *vS; /* the vertical scroller */
99 unsigned int vpos; /* the current vertical position */
100 unsigned int prevVpos; /* the previous vertical position */
102 WMScroller *hS; /* the horizontal scroller */
103 unsigned int hpos; /* the current horizontal position */
104 unsigned int prevHpos; /* the previous horizontal position */
106 WMFont *dFont; /* the default font */
107 WMColor *dColor; /* the default color */
108 WMPixmap *dBulletPix; /* the default pixmap for bullets */
110 GC bgGC; /* the background GC to draw with */
111 GC fgGC; /* the foreground GC to draw with */
112 GC stippledGC; /* the GC to overlay selected graphics with */
113 Pixmap db; /* the buffer on which to draw */
114 WMPixmap *bgPixmap; /* the background pixmap */
116 myRect visible; /* the actual rectangle that can be drawn into */
117 myRect cursor; /* the position and (height) of cursor */
118 myRect sel; /* the selection rectangle */
120 WMPoint clicked; /* where in the _document_ was clicked */
122 unsigned short tpos; /* the position in the currentTextBlock */
123 unsigned short docWidth; /* the width of the entire document */
124 unsigned int docHeight; /* the height of the entire document */
126 TextBlock *firstTextBlock;
127 TextBlock *lastTextBlock;
128 TextBlock *currentTextBlock;
130 WMArray *gfxItems; /* a nice array of graphic items */
132 #if DO_BLINK
133 WMHandlerID timerID; /* for nice twinky-winky */
134 #endif
136 WMAction *parser;
137 WMAction *writer;
138 WMTextDelegate *delegate;
139 Time lastClickTime;
141 WMRulerMargins *margins; /* an array of margins */
143 unsigned int nMargins:7; /* the total number of margins in use */
144 struct {
145 unsigned int monoFont:1; /* whether to ignore formats and graphic */
146 unsigned int focused:1; /* whether this instance has input focus */
147 unsigned int editable:1; /* "silly user, you can't edit me" */
148 unsigned int ownsSelection:1; /* "I ownz the current selection!" */
149 unsigned int pointerGrabbed:1;/* "heh, gib me pointer" */
150 unsigned int extendSelection:1; /* shift-drag to select more regions */
152 unsigned int rulerShown:1; /* whether the ruler is shown or not */
153 unsigned int frozen:1; /* whether screen updates are to be made */
154 unsigned int cursorShown:1; /* whether to show the cursor */
155 unsigned int acceptsGraphic:1;/* accept graphic when dropped */
156 unsigned int horizOnDemand:1;/* if a large image should appear*/
157 unsigned int needsLayOut:1; /* in case of Append/Deletes */
158 unsigned int ignoreNewLine:1;/* turn it into a ' ' in streams > 1 */
159 unsigned int indentNewLine:1;/* add " " for a newline typed */
160 unsigned int laidOut:1; /* have the TextBlocks all been laid out */
161 unsigned int waitingForSelection:1; /* I don't wanna wait in vain... */
162 unsigned int prepend:1; /* prepend=1, append=0 (for parsers) */
163 WMAlignment alignment:2; /* the alignment for text */
164 WMReliefType relief:3; /* the relief to display with */
165 unsigned int isOverGraphic:2;/* the mouse is over a graphic */
166 unsigned int first:1; /* for plain text parsing, newline? */
167 /* unsigned int RESERVED:1; */
168 } flags;
169 } Text;
172 #define NOTIFY(T,C,N,A) {\
173 WMNotification *notif = WMCreateNotification(N,T,A);\
174 if ((T)->delegate && (T)->delegate->C)\
175 (*(T)->delegate->C)((T)->delegate,notif);\
176 WMPostNotification(notif);\
177 WMReleaseNotification(notif);}
180 #define TYPETEXT 0
182 /* just to print blocks of text not terminated by \0 */
183 static void
184 output(char *ptr, int len)
186 char s[len+1];
187 memcpy(s, ptr, len);
188 s[len] = 0;
189 /* printf(" s is [%s] (%d)\n", s, strlen(s)); */
190 printf("[%s]\n", s);
194 #if DO_BLINK
195 #define CURSOR_BLINK_ON_DELAY 600
196 #define CURSOR_BLINK_OFF_DELAY 400
197 #endif
200 #define STIPPLE_WIDTH 8
201 #define STIPPLE_HEIGHT 8
202 static unsigned char STIPPLE_BITS[] = {
203 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa
208 static char *default_bullet[] = {
209 "6 6 4 1",
210 " c None s None", ". c black",
211 "X c white", "o c #808080",
212 " ... ",
213 ".XX.. ",
214 ".XX..o",
215 ".....o",
216 " ...oo",
217 " ooo "};
220 static void handleEvents(XEvent *event, void *data);
221 static void layOutDocument(Text *tPtr);
222 static void updateScrollers(Text *tPtr);
225 static int
226 getMarginNumber(Text *tPtr, WMRulerMargins *margins)
228 unsigned int i=0;
230 for(i=0; i < tPtr->nMargins; i++) {
232 if(WMIsMarginEqualToMargin(&tPtr->margins[i], margins))
233 return i;
236 return -1;
241 static int
242 newMargin(Text *tPtr, WMRulerMargins *margins)
244 int n;
246 if (!margins) {
247 tPtr->margins[0].retainCount++;
248 return 0;
251 n = getMarginNumber(tPtr, margins);
253 if (n == -1) {
255 if(tPtr->nMargins >= 127) {
256 n = tPtr->nMargins-1;
257 return n;
260 tPtr->margins = wrealloc(tPtr->margins,
261 (++tPtr->nMargins)*sizeof(WMRulerMargins));
263 n = tPtr->nMargins-1;
264 tPtr->margins[n].left = margins->left;
265 tPtr->margins[n].first = margins->first;
266 tPtr->margins[n].body = margins->body;
267 tPtr->margins[n].right = margins->right;
268 /* for each tab... */
269 tPtr->margins[n].retainCount = 1;
270 } else {
271 tPtr->margins[n].retainCount++;
274 return n;
277 static Bool
278 sectionWasSelected(Text *tPtr, TextBlock *tb, XRectangle *rect, int s)
280 unsigned short i, w, lw, selected = False, extend = False;
281 myRect sel;
284 /* if selection rectangle completely encloses the section */
285 if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
286 && (tb->sections[s]._y + tb->sections[s].h
287 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
288 sel.x = 0;
289 sel.w = tPtr->visible.w;
290 selected = extend = True;
292 /* or if it starts on a line and then goes further down */
293 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
294 && (tb->sections[s]._y + tb->sections[s].h
295 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
296 && (tb->sections[s]._y + tb->sections[s].h
297 >= tPtr->visible.y + tPtr->sel.y) ) {
298 sel.x = WMAX(tPtr->sel.x, tPtr->clicked.x);
299 sel.w = tPtr->visible.w;
300 selected = extend = True;
302 /* or if it begins before a line, but ends on it */
303 } else if ((tb->sections[s]._y >= tPtr->visible.y + tPtr->sel.y)
304 && (tb->sections[s]._y + tb->sections[s].h
305 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h)
306 && (tb->sections[s]._y
307 <= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
309 if (1||tPtr->sel.x + tPtr->sel.w > tPtr->clicked.x)
310 sel.w = tPtr->sel.x + tPtr->sel.w;
311 else
312 sel.w = tPtr->sel.x;
314 sel.x = 0;
315 selected = True;
317 /* or if the selection rectangle lies entirely within a line */
318 } else if ((tb->sections[s]._y <= tPtr->visible.y + tPtr->sel.y)
319 && (tPtr->sel.w >= 2)
320 && (tb->sections[s]._y + tb->sections[s].h
321 >= tPtr->visible.y + tPtr->sel.y + tPtr->sel.h) ) {
322 sel.x = tPtr->sel.x;
323 sel.w = tPtr->sel.w;
324 selected = True;
327 if (selected) {
328 selected = False;
330 /* if not within (modified) selection rectangle */
331 if ( tb->sections[s].x > sel.x + sel.w
332 || tb->sections[s].x + tb->sections[s].w < sel.x)
333 return False;
335 if (tb->graphic) {
336 if ( tb->sections[s].x + tb->sections[s].w <= sel.x + sel.w
337 && tb->sections[s].x >= sel.x) {
338 rect->width = tb->sections[s].w;
339 rect->x = tb->sections[s].x;
340 selected = True;
342 } else {
344 i = tb->sections[s].begin;
345 lw = 0;
347 if (0&& tb->sections[s].x >= sel.x) {
348 tb->s_begin = tb->sections[s].begin;
349 goto _selEnd;
352 while (++i <= tb->sections[s].end) {
354 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
355 lw += w;
357 if (lw + tb->sections[s].x >= sel.x
358 || i == tb->sections[s].end ) {
359 lw -= w;
360 i--;
361 tb->s_begin = (tb->selected? WMIN(tb->s_begin, i) : i);
362 break;
366 if (i > tb->sections[s].end) {
367 printf("WasSelected: (i > tb->sections[s].end) \n");
368 return False;
371 _selEnd: rect->x = tb->sections[s].x + lw;
372 lw = 0;
373 while(++i <= tb->sections[s].end) {
375 w = WMWidthOfString(tb->d.font, &(tb->text[i-1]), 1);
376 lw += w;
378 if (lw + rect->x >= sel.x + sel.w
379 || i == tb->sections[s].end ) {
381 if (i != tb->sections[s].end) {
382 lw -= w;
383 i--;
386 rect->width = lw;
387 if (tb->sections[s].last && sel.x + sel.w
388 >= tb->sections[s].x + tb->sections[s].w
389 && extend ) {
390 rect->width += (tPtr->visible.w - rect->x - lw);
393 tb->s_end = (tb->selected? WMAX(tb->s_end, i) : i);
394 selected = True;
395 break;
401 if (selected) {
402 rect->y = tb->sections[s]._y - tPtr->vpos;
403 rect->height = tb->sections[s].h;
404 if(tb->graphic) { printf("DEBUG: graphic s%d h%d\n", s,tb->sections[s].h);}
406 return selected;
410 static void
411 setSelectionProperty(WMText *tPtr, WMFont *font, WMColor *color, int underlined)
413 TextBlock *tb;
414 int isFont=False;
416 tb = tPtr->firstTextBlock;
417 if (!tb || !tPtr->flags.ownsSelection)
418 return;
420 if(font && (!color || underlined==-1))
421 isFont = True;
423 while (tb) {
424 if (tPtr->flags.monoFont || tb->selected) {
426 if (tPtr->flags.monoFont || (tb->s_end - tb->s_begin == tb->used)
427 || tb->graphic) {
429 if(isFont) {
430 if(!tb->graphic) {
431 WMReleaseFont(tb->d.font);
432 tb->d.font = WMRetainFont(font);
434 } else if(underlined !=-1) {
435 tb->underlined = underlined;
436 } else {
437 WMReleaseColor(tb->color);
438 tb->color = WMRetainColor(color);
441 } else if (tb->s_end <= tb->used && tb->s_begin < tb->s_end) {
443 TextBlock *midtb, *otb = tb;
445 if(underlined != -1) {
446 midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
447 &(tb->text[tb->s_begin]), tb->d.font, tb->color,
448 False, (tb->s_end - tb->s_begin));
449 } else {
450 midtb = (TextBlock *) WMCreateTextBlockWithText(tPtr,
451 &(tb->text[tb->s_begin]),
452 (isFont?font:tb->d.font),
453 (isFont?tb->color:color),
454 False, (tb->s_end - tb->s_begin));
458 if (midtb) {
459 if(underlined != -1) {
460 midtb->underlined = underlined;
461 } else {
462 midtb->underlined = otb->underlined;
465 midtb->selected = !True;
466 midtb->s_begin = 0;
467 midtb->s_end = midtb->used;
468 tPtr->currentTextBlock = tb;
469 WMAppendTextBlock(tPtr, midtb);
470 tb = tPtr->currentTextBlock;
473 if (otb->used - otb->s_end > 0) {
474 TextBlock *ntb;
475 ntb = (TextBlock *)
476 WMCreateTextBlockWithText(tPtr,
477 &(otb->text[otb->s_end]), otb->d.font, otb->color,
478 False, otb->used - otb->s_end);
480 if (ntb) {
481 ntb->underlined = otb->underlined;
482 ntb->selected = False;
483 WMAppendTextBlock(tPtr, ntb);
484 tb = tPtr->currentTextBlock;
488 if (midtb) {
489 tPtr->currentTextBlock = midtb;
492 otb->selected = False;
493 otb->used = otb->s_begin;
497 tb = tb->next;
500 tPtr->flags.needsLayOut = True;
501 WMThawText(tPtr);
503 /* in case the size changed... */
504 if(isFont && tPtr->currentTextBlock) {
505 TextBlock *tb = tPtr->currentTextBlock;
507 printf("%d %d %d\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
508 tPtr->sel.y = 3 + tb->sections[0]._y;
509 tPtr->sel.h = tb->sections[tb->nsections-1]._y - tb->sections[0]._y;
510 tPtr->sel.w = tb->sections[tb->nsections-1].w;
511 if(tb->sections[tb->nsections-1]._y != tb->sections[0]._y) {
512 tPtr->sel.x = 0;
514 printf("%d %d %d\n\n\n", tPtr->sel.y, tPtr->sel.h, tPtr->sel.w);
520 static Bool
521 removeSelection(Text *tPtr)
523 TextBlock *tb = NULL;
524 Bool first = False;
526 if (!(tb = tPtr->firstTextBlock))
527 return False;
529 while (tb) {
530 if (tb->selected) {
531 if(!first && !tb->graphic) {
532 WMReleaseFont(tPtr->dFont);
533 tPtr->dFont = WMRetainFont(tb->d.font);
534 first = True;
537 if ( (tb->s_end - tb->s_begin == tb->used) || tb->graphic) {
538 tPtr->currentTextBlock = tb;
539 if(tb->next) {
540 tPtr->tpos = 0;
541 } else if(tb->prior) {
542 if(tb->prior->graphic)
543 tPtr->tpos = 1;
544 else
545 tPtr->tpos = tb->prior->used;
546 } else tPtr->tpos = 0;
548 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
549 tb = tPtr->currentTextBlock;
550 continue;
552 } else if (tb->s_end <= tb->used) {
553 memmove(&(tb->text[tb->s_begin]),
554 &(tb->text[tb->s_end]), tb->used - tb->s_end);
555 tb->used -= (tb->s_end - tb->s_begin);
556 tb->selected = False;
557 tPtr->tpos = tb->s_begin;
562 tb = tb->next;
564 return True;
567 static TextBlock *
568 getFirstNonGraphicBlockFor(TextBlock *tb, short dir)
570 TextBlock *hold = tb;
572 if (!tb)
573 return NULL;
575 while (tb) {
576 if (!tb->graphic)
577 break;
578 tb = (dir? tb->next : tb->prior);
581 if(!tb) {
582 tb = hold;
583 while (tb) {
584 if (!tb->graphic)
585 break;
586 tb = (dir? tb->prior : tb->next);
590 if(!tb)
591 return NULL;
593 return tb;
597 static Bool
598 updateStartForCurrentTextBlock(Text *tPtr, int x, int y, int *dir,
599 TextBlock *tb)
601 if (tPtr->flags.monoFont && tb->graphic) {
602 tb = getFirstNonGraphicBlockFor(tb, *dir);
603 if(!tb)
604 return 0;
606 if (tb->graphic) {
607 tPtr->currentTextBlock =
608 (dir? tPtr->lastTextBlock : tPtr->firstTextBlock);
609 tPtr->tpos = 0;
610 return 0;
615 if(!tb->sections)
616 layOutDocument(tPtr);
617 if(!tb->sections)
618 return 0;
620 *dir = !(y <= tb->sections[0].y);
621 if(*dir) {
622 if ( ( y <= tb->sections[0]._y + tb->sections[0].h )
623 && (y >= tb->sections[0]._y ) ) {
624 /* if it's on the same line */
625 if(x < tb->sections[0].x)
626 *dir = 0;
628 } else {
629 if ( ( y <= tb->sections[tb->nsections-1]._y
630 + tb->sections[tb->nsections-1].h )
631 && (y >= tb->sections[tb->nsections-1]._y ) ) {
632 /* if it's on the same line */
633 if(x > tb->sections[tb->nsections-1].x)
634 *dir = 1;
638 return 1;
642 static void
643 paintText(Text *tPtr)
645 TextBlock *tb;
646 WMFont *font;
647 GC gc, greyGC;
648 char *text;
649 int len, y, c, s, done=False, prev_y=-23, dir /* 1 = down */;
650 WMScreen *scr = tPtr->view->screen;
651 Display *dpy = tPtr->view->screen->display;
652 Window win = tPtr->view->window;
653 WMColor *color;
655 if (!tPtr->view->flags.realized || !tPtr->db || tPtr->flags.frozen)
656 return;
659 XFillRectangle(dpy, tPtr->db, tPtr->bgGC,
660 0, 0, tPtr->visible.w, tPtr->visible.h);
662 if (tPtr->bgPixmap) {
663 WMDrawPixmap(tPtr->bgPixmap, tPtr->db,
664 (tPtr->visible.w-tPtr->visible.x-tPtr->bgPixmap->width)/2,
665 (tPtr->visible.h-tPtr->visible.y-tPtr->bgPixmap->height)/2);
668 if (! (tb = tPtr->currentTextBlock)) {
669 if (! (tb = tPtr->firstTextBlock)) {
670 goto _copy_area;
674 if (tPtr->flags.ownsSelection) {
675 color = WMGrayColor(scr);
676 greyGC = WMColorGC(color);
677 WMReleaseColor(color);
680 done = False;
684 /* first, which direction? Don't waste time looking all over,
685 since the parts to be drawn will most likely be near what
686 was previously drawn */
687 if(!updateStartForCurrentTextBlock(tPtr, 0, tPtr->vpos, &dir, tb))
688 goto _copy_area;
690 while(tb) {
692 if (tb->graphic && tPtr->flags.monoFont)
693 goto _getSibling;
695 if(dir) {
696 if(tPtr->vpos <= tb->sections[tb->nsections-1]._y
697 + tb->sections[tb->nsections-1].h)
698 break;
699 } else {
700 if(tPtr->vpos >= tb->sections[tb->nsections-1]._y
701 + tb->sections[tb->nsections-1].h)
702 break;
705 _getSibling:
706 if(dir) {
707 if(tb->next)
708 tb = tb->next;
709 else break;
710 } else {
711 if(tb->prior)
712 tb = tb->prior;
713 else break;
718 /* first, place all text that can be viewed */
719 while (!done && tb) {
722 if (tb->graphic) {
723 tb = tb->next;
724 continue;
727 tb->selected = False;
729 for(s=0; s<tb->nsections && !done; s++) {
731 if (tb->sections[s]._y > tPtr->vpos + tPtr->visible.h) {
732 done = True;
733 break;
736 if ( tb->sections[s].y + tb->sections[s].h < tPtr->vpos)
737 continue;
739 if (tPtr->flags.monoFont) {
740 font = tPtr->dFont;
741 gc = tPtr->fgGC;
742 } else {
743 font = tb->d.font;
744 gc = WMColorGC(tb->color);
747 if (tPtr->flags.ownsSelection) {
748 XRectangle rect;
750 if ( sectionWasSelected(tPtr, tb, &rect, s)) {
751 tb->selected = True;
752 XFillRectangle(dpy, tPtr->db, greyGC,
753 rect.x, rect.y, rect.width, rect.height);
757 prev_y = tb->sections[s]._y;
759 len = tb->sections[s].end - tb->sections[s].begin;
760 text = &(tb->text[tb->sections[s].begin]);
761 y = tb->sections[s].y - tPtr->vpos;
762 WMDrawString(scr, tPtr->db, gc, font,
763 tb->sections[s].x - tPtr->hpos, y, text, len);
765 if (!tPtr->flags.monoFont && tb->underlined) {
766 XDrawLine(dpy, tPtr->db, gc,
767 tb->sections[s].x - tPtr->hpos,
768 y + font->y + 1,
769 tb->sections[s].x + tb->sections[s].w - tPtr->hpos,
770 y + font->y + 1);
775 tb = (!done? tb->next : NULL);
779 /* now , show all graphic items that can be viewed */
780 c = WMGetArrayItemCount(tPtr->gfxItems);
781 if (c > 0 && !tPtr->flags.monoFont) {
782 int j, h;
784 for(j=0; j<c; j++) {
785 tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
787 /* if it's not viewable, and mapped, unmap it */
788 if (tb->sections[0]._y + tb->sections[0].h <= tPtr->vpos
789 || tb->sections[0]._y >= tPtr->vpos + tPtr->visible.h ) {
791 if(tb->object) {
792 if ((W_VIEW(tb->d.widget))->flags.mapped) {
793 WMUnmapWidget(tb->d.widget);
796 } else {
797 /* if it's viewable, and not mapped, map it */
798 if(tb->object) {
799 W_View *view = W_VIEW(tb->d.widget);
801 if (!view->flags.realized)
802 WMRealizeWidget(tb->d.widget);
803 if(!view->flags.mapped) {
804 XMapWindow(view->screen->display, view->window);
805 XFlush(view->screen->display);
806 view->flags.mapped = 1;
810 if(tb->object) {
811 WMMoveWidget(tb->d.widget,
812 tb->sections[0].x + tPtr->visible.x - tPtr->hpos,
813 tb->sections[0].y + tPtr->visible.y - tPtr->vpos);
814 h = WMWidgetHeight(tb->d.widget) + 1;
816 } else {
817 WMDrawPixmap(tb->d.pixmap, tPtr->db,
818 tb->sections[0].x - tPtr->hpos,
819 tb->sections[0].y - tPtr->vpos);
820 h = tb->d.pixmap->height + 1;
824 if (tPtr->flags.ownsSelection) {
825 XRectangle rect;
827 if ( sectionWasSelected(tPtr, tb, &rect, 0)) {
828 tb->selected = True;
829 XFillRectangle(dpy, tPtr->db, tPtr->stippledGC,
830 //XFillRectangle(dpy, tPtr->db, greyGC,
831 rect.x, rect.y, rect.width, rect.height);
835 if (!tPtr->flags.monoFont && tb->underlined) {
836 XDrawLine(dpy, tPtr->db, WMColorGC(tb->color),
837 tb->sections[0].x - tPtr->hpos,
838 tb->sections[0].y + h - tPtr->vpos,
839 tb->sections[0].x + tb->sections[0].w - tPtr->hpos,
840 tb->sections[0].y + h - tPtr->vpos);
847 _copy_area:
848 if (tPtr->flags.editable && tPtr->flags.cursorShown
849 && tPtr->cursor.x != -23 && tPtr->flags.focused) {
850 int y = tPtr->cursor.y - tPtr->vpos;
851 XDrawLine(dpy, tPtr->db, tPtr->fgGC,
852 tPtr->cursor.x, y,
853 tPtr->cursor.x, y + tPtr->cursor.h);
856 XCopyArea(dpy, tPtr->db, win, tPtr->bgGC, 0, 0,
857 tPtr->visible.w, tPtr->visible.h,
858 tPtr->visible.x, tPtr->visible.y);
860 W_DrawRelief(scr, win, 0, 0,
861 tPtr->view->size.width, tPtr->view->size.height,
862 tPtr->flags.relief);
864 if (tPtr->ruler && tPtr->flags.rulerShown)
865 XDrawLine(dpy, win, tPtr->fgGC,
866 2, 42, tPtr->view->size.width-4, 42);
870 static void
871 mouseOverObject(Text *tPtr, int x, int y)
873 TextBlock *tb;
874 Bool result = False;
876 x -= tPtr->visible.x;
877 x += tPtr->hpos;
878 y -= tPtr->visible.y;
879 y += tPtr->vpos;
881 if(tPtr->flags.ownsSelection) {
882 if(tPtr->sel.x <= x
883 && tPtr->sel.y <= y
884 && tPtr->sel.x + tPtr->sel.w >= x
885 && tPtr->sel.y + tPtr->sel.h >= y) {
886 tPtr->flags.isOverGraphic = 1;
887 result = True;
892 if(!result) {
893 int j, c = WMGetArrayItemCount(tPtr->gfxItems);
895 if (c<1)
896 tPtr->flags.isOverGraphic = 0;
899 for(j=0; j<c; j++) {
900 tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j);
902 if(!tb || !tb->sections) {
903 tPtr->flags.isOverGraphic = 0;
904 return;
907 if(!tb->object) {
908 if(tb->sections[0].x <= x
909 && tb->sections[0].y <= y
910 && tb->sections[0].x + tb->sections[0].w >= x
911 && tb->sections[0].y + tb->d.pixmap->height >= y ) {
912 tPtr->flags.isOverGraphic = 3;
913 result = True;
914 break;
922 if(!result)
923 tPtr->flags.isOverGraphic = 0;
926 tPtr->view->attribs.cursor = (result?
927 tPtr->view->screen->defaultCursor
928 : tPtr->view->screen->textCursor);
930 XSetWindowAttributes attribs;
931 attribs.cursor = tPtr->view->attribs.cursor;
932 XChangeWindowAttributes(tPtr->view->screen->display,
933 tPtr->view->window, CWCursor,
934 &attribs);
938 #if DO_BLINK
940 static void
941 blinkCursor(void *data)
943 Text *tPtr = (Text*)data;
945 if (tPtr->flags.cursorShown) {
946 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_OFF_DELAY,
947 blinkCursor, data);
948 } else {
949 tPtr->timerID = WMAddTimerHandler(CURSOR_BLINK_ON_DELAY,
950 blinkCursor, data);
952 paintText(tPtr);
953 tPtr->flags.cursorShown = !tPtr->flags.cursorShown;
955 #endif
957 static void
958 updateCursorPosition(Text *tPtr)
960 TextBlock *tb = NULL;
961 int x, y, h, s;
963 if(tPtr->flags.needsLayOut)
964 layOutDocument(tPtr);
966 if (! (tb = tPtr->currentTextBlock)) {
967 if (! (tb = tPtr->firstTextBlock)) {
968 WMFont *font = tPtr->dFont;
969 tPtr->tpos = 0;
970 tPtr->cursor.h = font->height + abs(font->height-font->y);
972 tPtr->cursor.y = 2;
973 tPtr->cursor.x = 2;
974 return;
979 if(tb->blank) {
980 tPtr->tpos = 0;
981 y = tb->sections[0].y;
982 h = tb->sections[0].h;
983 x = tb->sections[0].x;
985 } else if(tb->graphic) {
986 y = tb->sections[0].y;
987 h = tb->sections[0].h;
988 x = tb->sections[0].x;
989 if(tPtr->tpos == 1)
990 x += tb->sections[0].w;
992 } else {
993 if(tPtr->tpos > tb->used)
994 tPtr->tpos = tb->used;
996 for(s=0; s<tb->nsections-1; s++) {
998 if(tPtr->tpos >= tb->sections[s].begin
999 && tPtr->tpos <= tb->sections[s].end)
1000 break;
1003 y = tb->sections[s]._y;
1004 h = tb->sections[s].h;
1005 x = tb->sections[s].x + WMWidthOfString(
1006 (tPtr->flags.monoFont?tPtr->dFont:tb->d.font),
1007 &tb->text[tb->sections[s].begin],
1008 tPtr->tpos - tb->sections[s].begin);
1011 tPtr->cursor.y = y;
1012 tPtr->cursor.h = h;
1013 tPtr->cursor.x = x;
1016 /* scroll the bars if the cursor is not visible */
1017 if(tPtr->flags.editable && tPtr->cursor.x != -23) {
1018 if(tPtr->cursor.y+tPtr->cursor.h
1019 > tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
1020 tPtr->vpos +=
1021 (tPtr->cursor.y+tPtr->cursor.h+10
1022 - (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
1023 } else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
1024 tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
1029 updateScrollers(tPtr);
1033 static void
1034 cursorToTextPosition(Text *tPtr, int x, int y)
1036 TextBlock *tb = NULL;
1037 int done=False, s, pos, len, _w, _y, dir=1; /* 1 == "down" */
1038 char *text;
1040 if(tPtr->flags.needsLayOut)
1041 layOutDocument(tPtr);
1043 y += (tPtr->vpos - tPtr->visible.y);
1044 if (y<0)
1045 y = 0;
1047 x -= (tPtr->visible.x - 2);
1048 if (x<0)
1049 x=0;
1051 /* clicked is relative to document, not window... */
1052 tPtr->clicked.x = x;
1053 tPtr->clicked.y = y;
1055 if (! (tb = tPtr->currentTextBlock)) {
1056 if (! (tb = tPtr->firstTextBlock)) {
1057 WMFont *font = tPtr->dFont;
1058 tPtr->tpos = 0;
1059 tPtr->cursor.h = font->height + abs(font->height-font->y);
1060 tPtr->cursor.y = 2;
1061 tPtr->cursor.x = 2;
1062 return;
1066 /* first, which direction? Most likely, newly clicked
1067 position will be close to previous */
1068 if(!updateStartForCurrentTextBlock(tPtr, x, y, &dir, tb))
1069 return;
1072 s = (dir? 0 : tb->nsections-1);
1073 if ( y >= tb->sections[s]._y
1074 && y <= tb->sections[s]._y + tb->sections[s].h) {
1075 goto _doneV;
1078 /* get the first (or last) section of the TextBlock that
1079 lies about the vertical click point */
1080 done = False;
1081 while (!done && tb) {
1083 if (tPtr->flags.monoFont && tb->graphic) {
1084 if( (dir?tb->next:tb->prior))
1085 tb = (dir?tb->next:tb->prior);
1086 continue;
1089 s = (dir? 0 : tb->nsections-1);
1090 while (!done && (dir? (s<tb->nsections) : (s>=0) )) {
1092 if ( (dir? (y <= tb->sections[s]._y + tb->sections[s].h) :
1093 ( y >= tb->sections[s]._y ) ) ) {
1094 done = True;
1095 } else {
1096 dir? s++ : s--;
1100 if (!done) {
1101 if ( (dir? tb->next : tb->prior)) {
1102 tb = (dir ? tb->next : tb->prior);
1103 } else {
1104 pos = tb->used;
1105 break; /* goto _doneH; */
1111 if (s<0 || s>=tb->nsections) {
1112 s = (dir? tb->nsections-1 : 0);
1115 _doneV:
1116 /* we have the line, which TextBlock on that line is it? */
1117 pos = (dir?0:tb->sections[s].begin);
1118 if (tPtr->flags.monoFont && tb->graphic) {
1119 TextBlock *hold = tb;
1120 tb = getFirstNonGraphicBlockFor(hold, dir);
1122 if(!tb) {
1123 tPtr->tpos = 0;
1124 tb = hold;
1125 s = 0;
1126 goto _doNothing;
1131 if(tb->blank)
1132 _w = 0;
1134 _y = tb->sections[s]._y;
1136 while (tb) {
1138 if (tPtr->flags.monoFont && tb->graphic) {
1139 tb = (dir ? tb->next : tb->prior);
1140 continue;
1143 if (dir) {
1144 if (tb->graphic) {
1145 if(tb->object)
1146 _w = WMWidgetWidth(tb->d.widget)-5;
1147 else
1148 _w = tb->d.pixmap->width-5;
1150 if (tb->sections[0].x + _w >= x)
1151 break;
1152 } else {
1153 text = &(tb->text[tb->sections[s].begin]);
1154 len = tb->sections[s].end - tb->sections[s].begin;
1155 _w = WMWidthOfString(tb->d.font, text, len);
1156 if (tb->sections[s].x + _w >= x)
1157 break;
1160 } else {
1161 if (tb->sections[s].x <= x)
1162 break;
1165 if ((dir? tb->next : tb->prior)) {
1166 TextBlock *nxt = (dir? tb->next : tb->prior);
1167 if (tPtr->flags.monoFont && nxt->graphic) {
1168 nxt = getFirstNonGraphicBlockFor(nxt, dir);
1169 if (!nxt) {
1170 pos = (dir?0:tb->sections[s].begin);
1171 tPtr->cursor.x = tb->sections[s].x;
1172 goto _doneH;
1176 if (_y != nxt->sections[dir?0:nxt->nsections-1]._y) {
1177 /* this must be the last/first on this line. stop */
1178 pos = (dir? tb->sections[s].end : 0);
1179 tPtr->cursor.x = tb->sections[s].x;
1180 if (!tb->blank) {
1181 if (tb->graphic) {
1182 if(tb->object)
1183 tPtr->cursor.x += WMWidgetWidth(tb->d.widget);
1184 else
1185 tPtr->cursor.x += tb->d.pixmap->width;
1186 } else if (pos > tb->sections[s].begin) {
1187 tPtr->cursor.x +=
1188 WMWidthOfString(tb->d.font,
1189 &(tb->text[tb->sections[s].begin]),
1190 pos - tb->sections[s].begin);
1193 goto _doneH;
1197 if ( (dir? tb->next : tb->prior)) {
1198 tb = (dir ? tb->next : tb->prior);
1199 } else {
1200 done = True;
1201 break;
1204 if (tb)
1205 s = (dir? 0 : tb->nsections-1);
1208 /* we have said TextBlock, now where within it? */
1209 if (tb) {
1210 if(tb->graphic) {
1211 int gw = (tb->object ?
1212 WMWidgetWidth(tb->d.widget) : tb->d.pixmap->width);
1214 tPtr->cursor.x = tb->sections[0].x;
1216 if(x > tPtr->cursor.x + gw/2) {
1217 pos = 1;
1218 tPtr->cursor.x += gw;
1219 } else {
1220 printf("first %d\n", tb->first);
1221 if(tb->prior) {
1222 if(tb->prior->graphic) pos = 1;
1223 else pos = tb->prior->used;
1224 tb = tb->prior;
1225 } else pos = 0;
1229 s = 0;
1230 goto _doneH;
1232 } else {
1233 WMFont *f = tb->d.font;
1234 len = tb->sections[s].end - tb->sections[s].begin;
1235 text = &(tb->text[tb->sections[s].begin]);
1237 _w = x - tb->sections[s].x;
1238 pos = 0;
1240 while (pos<len && WMWidthOfString(f, text, pos+1) < _w)
1241 pos++;
1243 tPtr->cursor.x = tb->sections[s].x +
1244 (pos? WMWidthOfString(f, text, pos) : 0);
1246 pos += tb->sections[s].begin;
1250 _doneH:
1251 if(tb->graphic) {
1252 tPtr->tpos = (pos<=1)? pos : 0;
1253 } else {
1254 tPtr->tpos = (pos<tb->used)? pos : tb->used;
1256 _doNothing:
1257 if (!tb)
1258 printf("...for this app will surely crash :-)\n");
1260 tPtr->currentTextBlock = tb;
1261 tPtr->cursor.h = tb->sections[s].h;
1262 tPtr->cursor.y = tb->sections[s]._y;
1264 /* scroll the bars if the cursor is not visible */
1265 if(tPtr->flags.editable && tPtr->cursor.x != -23) {
1266 if(tPtr->cursor.y+tPtr->cursor.h
1267 > tPtr->vpos+tPtr->visible.y+tPtr->visible.h) {
1268 tPtr->vpos +=
1269 (tPtr->cursor.y+tPtr->cursor.h+10
1270 - (tPtr->vpos+tPtr->visible.y+tPtr->visible.h));
1271 updateScrollers(tPtr);
1272 } else if(tPtr->cursor.y < tPtr->vpos+tPtr->visible.y) {
1273 tPtr->vpos -= (tPtr->vpos+tPtr->visible.y-tPtr->cursor.y);
1274 updateScrollers(tPtr);
1282 static void
1283 updateScrollers(Text *tPtr)
1286 if (tPtr->flags.frozen)
1287 return;
1289 if (tPtr->vS) {
1290 if (tPtr->docHeight < tPtr->visible.h) {
1291 WMSetScrollerParameters(tPtr->vS, 0, 1);
1292 tPtr->vpos = 0;
1293 } else {
1294 float hmax = (float)(tPtr->docHeight);
1295 WMSetScrollerParameters(tPtr->vS,
1296 ((float)tPtr->vpos)/(hmax - (float)tPtr->visible.h),
1297 (float)tPtr->visible.h/hmax);
1299 } else tPtr->vpos = 0;
1301 if (tPtr->hS) {
1302 if (tPtr->docWidth < tPtr->visible.w) {
1303 WMSetScrollerParameters(tPtr->hS, 0, 1);
1304 tPtr->hpos = 0;
1305 } else {
1306 float wmax = (float)(tPtr->docWidth);
1307 WMSetScrollerParameters(tPtr->hS,
1308 ((float)tPtr->hpos)/(wmax - (float)tPtr->visible.w),
1309 (float)tPtr->visible.w/wmax);
1311 } else tPtr->hpos = 0;
1314 static void
1315 scrollersCallBack(WMWidget *w, void *self)
1317 Text *tPtr = (Text *)self;
1318 Bool scroll = False;
1319 int which;
1321 if (!tPtr->view->flags.realized || tPtr->flags.frozen)
1322 return;
1324 if (w == tPtr->vS) {
1325 int height;
1326 height = tPtr->visible.h;
1328 which = WMGetScrollerHitPart(tPtr->vS);
1329 switch(which) {
1331 case WSDecrementLine:
1332 if (tPtr->vpos > 0) {
1333 if (tPtr->vpos>16) tPtr->vpos-=16;
1334 else tPtr->vpos=0;
1335 scroll=True;
1337 break;
1339 case WSIncrementLine: {
1340 int limit = tPtr->docHeight - height;
1341 if (tPtr->vpos < limit) {
1342 if (tPtr->vpos<limit-16) tPtr->vpos+=16;
1343 else tPtr->vpos=limit;
1344 scroll = True;
1347 break;
1349 case WSDecrementPage:
1350 if(((int)tPtr->vpos - (int)height) >= 0)
1351 tPtr->vpos -= height;
1352 else
1353 tPtr->vpos = 0;
1355 scroll = True;
1356 break;
1358 case WSIncrementPage:
1359 tPtr->vpos += height;
1360 if (tPtr->vpos > (tPtr->docHeight - height))
1361 tPtr->vpos = tPtr->docHeight - height;
1362 scroll = True;
1363 break;
1366 case WSKnob:
1367 tPtr->vpos = WMGetScrollerValue(tPtr->vS)
1368 * (float)(tPtr->docHeight - height);
1369 scroll = True;
1370 break;
1372 case WSKnobSlot:
1373 case WSNoPart:
1374 break;
1376 scroll = (tPtr->vpos != tPtr->prevVpos);
1377 tPtr->prevVpos = tPtr->vpos;
1381 if (w == tPtr->hS) {
1382 int width = tPtr->visible.w;
1384 which = WMGetScrollerHitPart(tPtr->hS);
1385 switch(which) {
1387 case WSDecrementLine:
1388 if (tPtr->hpos > 0) {
1389 if (tPtr->hpos>16) tPtr->hpos-=16;
1390 else tPtr->hpos=0;
1391 scroll=True;
1392 }break;
1394 case WSIncrementLine: {
1395 int limit = tPtr->docWidth - width;
1396 if (tPtr->hpos < limit) {
1397 if (tPtr->hpos<limit-16) tPtr->hpos+=16;
1398 else tPtr->hpos=limit;
1399 scroll = True;
1401 }break;
1403 case WSDecrementPage:
1404 if(((int)tPtr->hpos - (int)width) >= 0)
1405 tPtr->hpos -= width;
1406 else
1407 tPtr->hpos = 0;
1409 scroll = True;
1410 break;
1412 case WSIncrementPage:
1413 tPtr->hpos += width;
1414 if (tPtr->hpos > (tPtr->docWidth - width))
1415 tPtr->hpos = tPtr->docWidth - width;
1416 scroll = True;
1417 break;
1420 case WSKnob:
1421 tPtr->hpos = WMGetScrollerValue(tPtr->hS)
1422 * (float)(tPtr->docWidth - width);
1423 scroll = True;
1424 break;
1426 case WSKnobSlot:
1427 case WSNoPart:
1428 break;
1430 scroll = (tPtr->hpos != tPtr->prevHpos);
1431 tPtr->prevHpos = tPtr->hpos;
1434 if (scroll) {
1435 updateScrollers(tPtr);
1436 paintText(tPtr);
1442 typedef struct {
1443 TextBlock *tb;
1444 unsigned short begin, end; /* what part of the text block */
1445 } myLineItems;
1448 static int
1449 layOutLine(Text *tPtr, myLineItems *items, int nitems, int x, int y)
1451 int i, j=0, lw = 0, line_height=0, max_d=0, len, n;
1452 WMFont *font;
1453 char *text;
1454 TextBlock *tb, *tbsame=NULL;
1456 if(!items || nitems == 0)
1457 return 0;
1459 for(i=0; i<nitems; i++) {
1460 tb = items[i].tb;
1462 if (tb->graphic) {
1463 if (!tPtr->flags.monoFont) {
1464 if(tb->object) {
1465 WMWidget *wdt = tb->d.widget;
1466 line_height = WMAX(line_height, WMWidgetHeight(wdt));
1467 if (tPtr->flags.alignment != WALeft)
1468 lw += WMWidgetWidth(wdt);
1469 } else {
1470 line_height = WMAX(line_height,
1471 tb->d.pixmap->height + max_d);
1472 if (tPtr->flags.alignment != WALeft)
1473 lw += tb->d.pixmap->width;
1477 } else {
1478 font = (tPtr->flags.monoFont)?tPtr->dFont : tb->d.font;
1479 max_d = WMAX(max_d, abs(font->height-font->y));
1480 line_height = WMAX(line_height, font->height + max_d);
1481 text = &(tb->text[items[i].begin]);
1482 len = items[i].end - items[i].begin;
1483 if (tPtr->flags.alignment != WALeft)
1484 lw += WMWidthOfString(font, text, len);
1488 if (tPtr->flags.alignment == WARight) {
1489 j = tPtr->visible.w - lw;
1490 } else if (tPtr->flags.alignment == WACenter) {
1491 j = (int) ((float)(tPtr->visible.w - lw))/2.0;
1494 for(i=0; i<nitems; i++) {
1495 tb = items[i].tb;
1497 if (tbsame == tb) { /* extend it, since it's on same line */
1498 tb->sections[tb->nsections-1].end = items[i].end;
1499 n = tb->nsections-1;
1500 } else {
1501 tb->sections = wrealloc(tb->sections,
1502 (++tb->nsections)*sizeof(Section));
1503 n = tb->nsections-1;
1504 tb->sections[n]._y = y + max_d;
1505 tb->sections[n].max_d = max_d;
1506 tb->sections[n].x = x+j;
1507 tb->sections[n].h = line_height;
1508 tb->sections[n].begin = items[i].begin;
1509 tb->sections[n].end = items[i].end;
1512 tb->sections[n].last = (i+1 == nitems);
1514 if (tb->graphic) {
1515 if (!tPtr->flags.monoFont) {
1516 if(tb->object) {
1517 WMWidget *wdt = tb->d.widget;
1518 tb->sections[n].y = max_d + y
1519 + line_height - WMWidgetHeight(wdt);
1520 tb->sections[n].w = WMWidgetWidth(wdt);
1521 } else {
1522 tb->sections[n].y = y + line_height
1523 + max_d - tb->d.pixmap->height;
1524 tb->sections[n].w = tb->d.pixmap->width;
1526 x += tb->sections[n].w;
1528 } else {
1529 font = (tPtr->flags.monoFont)? tPtr->dFont : tb->d.font;
1530 len = items[i].end - items[i].begin;
1531 text = &(tb->text[items[i].begin]);
1533 tb->sections[n].y = y+line_height-font->y;
1534 tb->sections[n].w =
1535 WMWidthOfString(font,
1536 &(tb->text[tb->sections[n].begin]),
1537 tb->sections[n].end - tb->sections[n].begin);
1539 x += WMWidthOfString(font, text, len);
1542 tbsame = tb;
1545 return line_height;
1550 static void
1551 layOutDocument(Text *tPtr)
1553 TextBlock *tb;
1554 myLineItems *items = NULL;
1555 unsigned int itemsSize=0, nitems=0, begin, end;
1556 WMFont *font;
1557 unsigned int x, y=0, lw = 0, width=0, bmargin;
1558 char *start=NULL, *mark=NULL;
1560 if ( tPtr->flags.frozen || (!(tb = tPtr->firstTextBlock)) )
1561 return;
1563 assert(tPtr->visible.w > 20);
1565 tPtr->docWidth = tPtr->visible.w;
1566 x = tPtr->margins[tb->marginN].first;
1567 bmargin = tPtr->margins[tb->marginN].body;
1569 /* only partial layOut needed: re-Lay only affected textblocks */
1570 if (tPtr->flags.laidOut) {
1571 tb = tPtr->currentTextBlock;
1573 /* search backwards for textblocks on same line */
1574 while (tb->prior) {
1575 if (!tb->sections || tb->nsections<1) {
1576 tb = tPtr->firstTextBlock;
1577 tPtr->flags.laidOut = False;
1578 y = 0;
1579 goto _layOut;
1582 if(!tb->prior->sections || tb->prior->nsections<1) {
1583 tb = tPtr->firstTextBlock;
1584 tPtr->flags.laidOut = False;
1585 y = 0;
1586 goto _layOut;
1589 if (tb->sections[0]._y !=
1590 tb->prior->sections[tb->prior->nsections-1]._y) {
1591 break;
1593 tb = tb->prior;
1596 if(tb->prior && tb->prior->sections && tb->prior->nsections>0) {
1597 y = tb->prior->sections[tb->prior->nsections-1]._y +
1598 tb->prior->sections[tb->prior->nsections-1].h -
1599 tb->prior->sections[tb->prior->nsections-1].max_d;
1600 } else {
1601 y = 0;
1605 _layOut:
1606 while (tb) {
1608 if (tb->sections && tb->nsections>0) {
1609 wfree(tb->sections);
1610 tb->sections = NULL;
1611 tb->nsections = 0;
1614 if (tb->first && tb->blank && tb->next && !tb->next->first) {
1615 TextBlock *next = tb->next;
1616 tPtr->currentTextBlock = tb;
1617 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1618 tb = next;
1619 tb->first = True;
1620 continue;
1623 if (tb->first && tb != tPtr->firstTextBlock) {
1624 y += layOutLine(tPtr, items, nitems, x, y);
1625 x = tPtr->margins[tb->marginN].first;
1626 bmargin = tPtr->margins[tb->marginN].body;
1627 nitems = 0;
1628 lw = 0;
1631 if (tb->graphic) {
1632 if (!tPtr->flags.monoFont) {
1633 if(tb->object)
1634 width = WMWidgetWidth(tb->d.widget);
1635 else
1636 width = tb->d.pixmap->width;
1638 if (width > tPtr->docWidth)
1639 tPtr->docWidth = width;
1641 lw += width;
1642 if (lw >= tPtr->visible.w - x ) {
1643 y += layOutLine(tPtr, items, nitems, x, y);
1644 nitems = 0;
1645 x = bmargin;
1646 lw = width;
1649 if(nitems + 1> itemsSize) {
1650 items = wrealloc(items,
1651 (++itemsSize)*sizeof(myLineItems));
1654 items[nitems].tb = tb;
1655 items[nitems].begin = 0;
1656 items[nitems].end = 0;
1657 nitems++;
1660 } else if ((start = tb->text)) {
1661 begin = end = 0;
1662 font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
1664 while (start) {
1665 mark = strchr(start, ' ');
1666 if (mark) {
1667 end += (int)(mark-start)+1;
1668 start = mark+1;
1669 } else {
1670 end += strlen(start);
1671 start = mark;
1674 if (end > tb->used)
1675 end = tb->used;
1677 if (end-begin > 0) {
1679 width = WMWidthOfString(font,
1680 &tb->text[begin], end-begin);
1682 /* if it won't fit, char wrap it */
1683 if (width >= tPtr->visible.w) {
1684 char *t = &tb->text[begin];
1685 int l=end-begin, i=0;
1686 do {
1687 width = WMWidthOfString(font, t, ++i);
1688 } while (width < tPtr->visible.w && i < l);
1689 if(i>2) i--;
1690 end = begin+i;
1691 start = &tb->text[end];
1694 lw += width;
1697 if (lw >= tPtr->visible.w - x) {
1698 y += layOutLine(tPtr, items, nitems, x, y);
1699 lw = width;
1700 x = bmargin;
1701 nitems = 0;
1704 if(nitems + 1 > itemsSize) {
1705 items = wrealloc(items,
1706 (++itemsSize)*sizeof(myLineItems));
1709 items[nitems].tb = tb;
1710 items[nitems].begin = begin;
1711 items[nitems].end = end;
1712 nitems++;
1714 begin = end;
1719 /* not yet fully ready. but is already VERY FAST for a 3Mbyte file ;-) */
1720 if(0&&tPtr->flags.laidOut
1721 && tb->next && tb->next->sections && tb->next->nsections>0
1722 && (tPtr->vpos + tPtr->visible.h
1723 < tb->next->sections[0]._y)) {
1724 if(tPtr->lastTextBlock->sections
1725 && tPtr->lastTextBlock->nsections > 0 ) {
1726 TextBlock *ltb = tPtr->lastTextBlock;
1727 int ly = ltb->sections[ltb->nsections-1]._y;
1728 int lh = ltb->sections[ltb->nsections-1].h;
1729 int ss, sd;
1731 lh += 1 + tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d;
1732 printf("it's %d\n", tPtr->visible.y + ltb->sections[ltb->nsections-1].max_d);
1734 y += layOutLine(tPtr, items, nitems, x, y);
1735 ss= ly+lh-y;
1736 sd = tPtr->docHeight-y;
1738 printf("dif %d-%d: %d\n", ss, sd, ss-sd);
1739 y += tb->next->sections[0]._y-y;
1740 nitems = 0;
1741 printf("nitems%d\n", nitems);
1742 if(ss-sd!=0)
1743 y = tPtr->docHeight+ss-sd;
1745 break;
1746 } else {
1747 tPtr->flags.laidOut = False;
1751 tb = tb->next;
1755 if (nitems > 0)
1756 y += layOutLine(tPtr, items, nitems, x, y);
1758 if (tPtr->docHeight != y+10) {
1759 tPtr->docHeight = y+10;
1760 updateScrollers(tPtr);
1763 if(tPtr->docWidth > tPtr->visible.w && !tPtr->hS) {
1764 XEvent event;
1766 tPtr->flags.horizOnDemand = True;
1767 WMSetTextHasHorizontalScroller((WMText*)tPtr, True);
1768 event.type = Expose;
1769 handleEvents(&event, (void *)tPtr);
1771 } else if(tPtr->docWidth <= tPtr->visible.w
1772 && tPtr->hS && tPtr->flags.horizOnDemand ) {
1773 tPtr->flags.horizOnDemand = False;
1774 WMSetTextHasHorizontalScroller((WMText*)tPtr, False);
1777 tPtr->flags.laidOut = True;
1779 if(items && itemsSize > 0)
1780 wfree(items);
1785 static void
1786 textDidResize(W_ViewDelegate *self, WMView *view)
1788 Text *tPtr = (Text *)view->self;
1789 unsigned short w = tPtr->view->size.width;
1790 unsigned short h = tPtr->view->size.height;
1791 unsigned short rh = 0, vw = 0, rel;
1793 rel = (tPtr->flags.relief == WRFlat);
1795 if (tPtr->ruler && tPtr->flags.rulerShown) {
1796 WMMoveWidget(tPtr->ruler, 2, 2);
1797 WMResizeWidget(tPtr->ruler, w - 4, 40);
1798 rh = 40;
1801 if (tPtr->vS) {
1802 WMMoveWidget(tPtr->vS, 1 - (rel?1:0), rh + 1 - (rel?1:0));
1803 WMResizeWidget(tPtr->vS, 20, h - rh - 2 + (rel?2:0));
1804 vw = 20;
1805 WMSetRulerOffset(tPtr->ruler,22);
1806 } else WMSetRulerOffset(tPtr->ruler, 2);
1808 if (tPtr->hS) {
1809 if (tPtr->vS) {
1810 WMMoveWidget(tPtr->hS, vw, h - 21);
1811 WMResizeWidget(tPtr->hS, w - vw - 1, 20);
1812 } else {
1813 WMMoveWidget(tPtr->hS, vw+1, h - 21);
1814 WMResizeWidget(tPtr->hS, w - vw - 2, 20);
1818 tPtr->visible.x = (tPtr->vS)?24:4;
1819 tPtr->visible.y = (tPtr->ruler && tPtr->flags.rulerShown)?43:3;
1820 tPtr->visible.w = tPtr->view->size.width - tPtr->visible.x - 8;
1821 tPtr->visible.h = tPtr->view->size.height - tPtr->visible.y;
1822 tPtr->visible.h -= (tPtr->hS)?20:0;
1823 tPtr->margins[0].right = tPtr->visible.w;
1825 if (tPtr->view->flags.realized) {
1827 if (tPtr->db) {
1828 XFreePixmap(tPtr->view->screen->display, tPtr->db);
1829 tPtr->db = (Pixmap) NULL;
1832 if (tPtr->visible.w < 40)
1833 tPtr->visible.w = 40;
1834 if (tPtr->visible.h < 20)
1835 tPtr->visible.h = 20;
1837 if(!tPtr->db) {
1838 tPtr->db = XCreatePixmap(tPtr->view->screen->display,
1839 tPtr->view->window, tPtr->visible.w,
1840 tPtr->visible.h, tPtr->view->screen->depth);
1844 WMThawText(tPtr);
1847 W_ViewDelegate _TextViewDelegate =
1849 NULL,
1850 NULL,
1851 textDidResize,
1852 NULL,
1855 #define TEXT_BUFFER_INCR 8
1856 #define reqBlockSize(requested) (requested + TEXT_BUFFER_INCR)
1858 static void
1859 clearText(Text *tPtr)
1861 tPtr->vpos = tPtr->hpos = 0;
1862 tPtr->docHeight = tPtr->docWidth = 0;
1863 tPtr->cursor.x = -23;
1865 if (!tPtr->firstTextBlock)
1866 return;
1868 while (tPtr->currentTextBlock)
1869 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1871 tPtr->firstTextBlock = NULL;
1872 tPtr->currentTextBlock = NULL;
1873 tPtr->lastTextBlock = NULL;
1874 WMEmptyArray(tPtr->gfxItems);
1877 /* possibly remove a single character from the currentTextBlock,
1878 or if there's a selection, remove it...
1879 note that Delete and Backspace are treated differently */
1880 static void
1881 deleteTextInteractively(Text *tPtr, KeySym ksym)
1883 TextBlock *tb;
1884 Bool back = (Bool) (ksym == XK_BackSpace);
1885 Bool done = 1, wasFirst = 0;
1887 if (!tPtr->flags.editable)
1888 return;
1890 if ( !(tb = tPtr->currentTextBlock) )
1891 return;
1893 if (tPtr->flags.ownsSelection) {
1894 if(removeSelection(tPtr))
1895 layOutDocument(tPtr);
1896 return;
1899 wasFirst = tb->first;
1900 if (back && tPtr->tpos < 1) {
1901 if (tb->prior) {
1902 if(tb->prior->blank) {
1903 tPtr->currentTextBlock = tb->prior;
1904 WMRemoveTextBlock(tPtr);
1905 tPtr->currentTextBlock = tb;
1906 tb->first = True;
1907 layOutDocument(tPtr);
1908 return;
1909 } else {
1910 if(tb->blank) {
1911 TextBlock *prior = tb->prior;
1912 tPtr->currentTextBlock = tb;
1913 WMRemoveTextBlock(tPtr);
1914 tb = prior;
1915 } else {
1916 tb = tb->prior;
1919 if(tb->graphic)
1920 tPtr->tpos = 1;
1921 else
1922 tPtr->tpos = tb->used;
1924 tPtr->currentTextBlock = tb;
1925 done = 1;
1926 if(wasFirst) {
1927 if(tb->next)
1928 tb->next->first = False;
1929 layOutDocument(tPtr);
1930 return;
1936 if ( (tb->used > 0) && ((back?tPtr->tpos > 0:1))
1937 && (tPtr->tpos <= tb->used) && !tb->graphic) {
1938 if (back)
1939 tPtr->tpos--;
1940 memmove(&(tb->text[tPtr->tpos]),
1941 &(tb->text[tPtr->tpos + 1]), tb->used - tPtr->tpos);
1942 tb->used--;
1943 done = 0;
1946 /* if there are no characters left to back over in the textblock,
1947 but it still has characters to the right of the cursor: */
1948 if ( (back? (tPtr->tpos == 0 && !done) : ( tPtr->tpos >= tb->used))
1949 || tb->graphic) {
1951 /* no more chars, and it's marked as blank? */
1952 if(tb->blank) {
1953 TextBlock *sibling = (back? tb->prior : tb->next);
1955 if(tb->used == 0 || tb->graphic)
1956 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1958 if (sibling) {
1959 tPtr->currentTextBlock = sibling;
1960 if(tb->graphic)
1961 tPtr->tpos = (back? 1 : 0);
1962 else
1963 tPtr->tpos = (back? sibling->used : 0);
1965 /* no more chars, so mark it as blank */
1966 } else if(tb->used == 0) {
1967 tb->blank = 1;
1968 } else if(tb->graphic) {
1969 Bool hasNext = (Bool)(tb->next);
1971 WMDestroyTextBlock(tPtr, WMRemoveTextBlock(tPtr));
1972 if(hasNext) {
1973 tPtr->tpos = 0;
1974 } else if(tPtr->currentTextBlock) {
1975 tPtr->tpos = (tPtr->currentTextBlock->graphic?
1976 1 : tPtr->currentTextBlock->used);
1978 } else printf("DEBUG: unaccounted for... catch this!\n");
1981 layOutDocument(tPtr);
1985 static void
1986 insertTextInteractively(Text *tPtr, char *text, int len)
1988 TextBlock *tb;
1989 char *newline = NULL;
1991 if (!tPtr->flags.editable) {
1992 return;
1995 if (len < 1 || !text)
1996 return;
1999 if(tPtr->flags.ignoreNewLine && *text == '\n' && len == 1)
2000 return;
2003 if (tPtr->flags.ownsSelection)
2004 removeSelection(tPtr);
2007 if (tPtr->flags.ignoreNewLine) {
2008 int i;
2009 for(i=0; i<len; i++) {
2010 if (text[i] == '\n')
2011 text[i] = ' ';
2015 tb = tPtr->currentTextBlock;
2016 if (!tb || tb->graphic) {
2017 tPtr->tpos = 0;
2018 WMAppendTextStream(tPtr, text);
2019 layOutDocument(tPtr);
2020 return;
2023 if ((newline = strchr(text, '\n'))) {
2024 int nlen = (int)(newline-text);
2025 int s = tb->used - tPtr->tpos;
2026 char save[s];
2027 if (!tb->blank && nlen>0) {
2028 if (s > 0) {
2029 memcpy(save, &tb->text[tPtr->tpos], s);
2030 tb->used -= (tb->used - tPtr->tpos);
2032 insertTextInteractively(tPtr, text, nlen);
2033 newline++;
2034 WMAppendTextStream(tPtr, newline);
2035 if (s>0)
2036 insertTextInteractively(tPtr, save, s);
2038 } else {
2039 if (tPtr->tpos>0 && tPtr->tpos < tb->used
2040 && !tb->graphic && tb->text) {
2042 void *ntb = WMCreateTextBlockWithText(
2043 tPtr, &tb->text[tPtr->tpos],
2044 tb->d.font, tb->color, True, tb->used - tPtr->tpos);
2045 tb->used = tPtr->tpos;
2046 WMAppendTextBlock(tPtr, ntb);
2047 tPtr->tpos = 0;
2049 } else if (tPtr->tpos == tb->used || tPtr->tpos == 0) {
2050 if(tPtr->flags.indentNewLine) {
2051 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2052 " ", tb->d.font, tb->color, True, 4));
2053 tPtr->tpos = 4;
2054 } else {
2055 WMAppendTextBlock(tPtr, WMCreateTextBlockWithText(tPtr,
2056 NULL, tb->d.font, tb->color, True, 0));
2057 tPtr->tpos = 0;
2062 } else {
2063 if (tb->used + len >= tb->allocated) {
2064 tb->allocated = reqBlockSize(tb->used+len);
2065 tb->text = wrealloc(tb->text, tb->allocated);
2068 if (tb->blank) {
2069 memcpy(tb->text, text, len);
2070 tb->used = len;
2071 tPtr->tpos = len;
2072 tb->text[tb->used] = 0;
2073 tb->blank = False;
2075 } else {
2076 memmove(&(tb->text[tPtr->tpos+len]), &tb->text[tPtr->tpos],
2077 tb->used-tPtr->tpos+1);
2078 memmove(&tb->text[tPtr->tpos], text, len);
2079 tb->used += len;
2080 tPtr->tpos += len;
2081 tb->text[tb->used] = 0;
2086 layOutDocument(tPtr);
2090 static void
2091 selectRegion(Text *tPtr, int x, int y)
2094 if (x < 0 || y < 0)
2095 return;
2097 y += (tPtr->flags.rulerShown? 40: 0);
2098 y += tPtr->vpos;
2099 if (y>10)
2100 y -= 10; /* the original offset */
2102 x -= tPtr->visible.x-2;
2103 if (x<0)
2104 x=0;
2106 tPtr->sel.x = WMAX(0, WMIN(tPtr->clicked.x, x));
2107 tPtr->sel.w = abs(tPtr->clicked.x - x);
2108 tPtr->sel.y = WMAX(0, WMIN(tPtr->clicked.y, y));
2109 tPtr->sel.h = abs(tPtr->clicked.y - y);
2111 tPtr->flags.ownsSelection = True;
2112 paintText(tPtr);
2116 static void
2117 releaseSelection(Text *tPtr)
2119 TextBlock *tb = tPtr->firstTextBlock;
2121 while(tb) {
2122 tb->selected = False;
2123 tb = tb->next;
2125 tPtr->flags.ownsSelection = False;
2126 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY,
2127 CurrentTime);
2129 paintText(tPtr);
2133 WMData*
2134 requestHandler(WMView *view, Atom selection, Atom target, void *cdata,
2135 Atom *type)
2137 Text *tPtr = view->self;
2138 Display *dpy = tPtr->view->screen->display;
2139 Atom _TARGETS;
2140 Atom TEXT = XInternAtom(dpy, "TEXT", False);
2141 Atom COMPOUND_TEXT = XInternAtom(dpy, "COMPOUND_TEXT", False);
2142 WMData *data = NULL;
2145 if (target == XA_STRING || target == TEXT || target == COMPOUND_TEXT) {
2146 char *text = WMGetTextSelectedStream(tPtr);
2148 if (text) {
2149 data = WMCreateDataWithBytes(text, strlen(text));
2150 WMSetDataFormat(data, TYPETEXT);
2152 *type = target;
2153 return data;
2154 } else printf("didn't get it\n");
2156 _TARGETS = XInternAtom(dpy, "TARGETS", False);
2157 if (target == _TARGETS) {
2158 Atom *ptr;
2160 ptr = wmalloc(4 * sizeof(Atom));
2161 ptr[0] = _TARGETS;
2162 ptr[1] = XA_STRING;
2163 ptr[2] = TEXT;
2164 ptr[3] = COMPOUND_TEXT;
2166 data = WMCreateDataWithBytes(ptr, 4*4);
2167 WMSetDataFormat(data, 32);
2169 *type = target;
2170 return data;
2173 return NULL;
2176 static void
2177 lostHandler(WMView *view, Atom selection, void *cdata)
2179 releaseSelection((WMText *)view->self);
2182 static WMSelectionProcs selectionHandler = {
2183 requestHandler, lostHandler, NULL
2187 static void
2188 ownershipObserver(void *observerData, WMNotification *notification)
2190 if (observerData != WMGetNotificationClientData(notification))
2191 lostHandler(WMWidgetView(observerData), XA_PRIMARY, NULL);
2194 static void
2195 autoSelectText(Text *tPtr, int clicks)
2197 int x, start;
2198 TextBlock *tb;
2199 char *mark = NULL, behind, ahead;
2201 if(!(tb = tPtr->currentTextBlock))
2202 return;
2204 if(clicks == 2) {
2207 switch(tb->text[tPtr->tpos]) {
2208 case ' ': return;
2210 case '<': case '>': behind = '<'; ahead = '>'; break;
2211 case '{': case '}': behind = '{'; ahead = '}'; break;
2212 case '[': case ']': behind = '['; ahead = ']'; break;
2214 default: behind = ahead = ' ';
2217 tPtr->sel.y = tPtr->cursor.y+5;
2218 tPtr->sel.h = 6;/*tPtr->cursor.h-10;*/
2220 if(tb->graphic) {
2221 tPtr->sel.x = tb->sections[0].x;
2222 tPtr->sel.w = tb->sections[0].w;
2223 } else {
2224 WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
2226 start = tPtr->tpos;
2227 while(start > 0 && tb->text[start-1] != behind)
2228 start--;
2230 x = tPtr->cursor.x;
2231 if(tPtr->tpos > start){
2232 x -= WMWidthOfString(font, &tb->text[start],
2233 tPtr->tpos - start);
2235 tPtr->sel.x = (x<0?0:x)+1;
2237 if((mark = strchr(&tb->text[start], ahead))) {
2238 tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
2239 (int)(mark - &tb->text[start]));
2240 } else if(tb->used > start) {
2241 tPtr->sel.w = WMWidthOfString(font, &tb->text[start],
2242 tb->used - start);
2246 } else if(clicks == 3) {
2247 TextBlock *cur = tb;
2249 while(tb && !tb->first) {
2250 tb = tb->prior;
2252 tPtr->sel.y = tb->sections[0]._y;
2254 tb = cur;
2255 while(tb->next && !tb->next->first) {
2256 tb = tb->next;
2258 tPtr->sel.h = tb->sections[tb->nsections-1]._y
2259 + 5 - tPtr->sel.y;
2261 tPtr->sel.x = 0;
2262 tPtr->sel.w = tPtr->docWidth;
2263 tPtr->clicked.x = 0; /* only for now, fix sel. code */
2266 if (!tPtr->flags.ownsSelection) {
2267 WMCreateSelectionHandler(tPtr->view,
2268 XA_PRIMARY, tPtr->lastClickTime, &selectionHandler, NULL);
2269 tPtr->flags.ownsSelection = True;
2271 paintText(tPtr);
2276 static void
2277 fontChanged(void *observerData, WMNotification *notification)
2279 WMText *tPtr = (WMText *) observerData;
2280 WMFont *font = (WMFont *)WMGetNotificationClientData(notification);
2281 printf("fontChanged\n");
2283 if(!tPtr || !font)
2284 return;
2286 if (tPtr->flags.ownsSelection)
2287 WMSetTextSelectionFont(tPtr, font);
2291 static void
2292 handleTextKeyPress(Text *tPtr, XEvent *event)
2294 char buffer[64];
2295 KeySym ksym;
2296 int control_pressed = False;
2297 TextBlock *tb = NULL;
2299 if (((XKeyEvent *) event)->state & ControlMask)
2300 control_pressed = True;
2301 buffer[XLookupString(&event->xkey, buffer, 63, &ksym, NULL)] = 0;
2303 switch(ksym) {
2305 case XK_Home:
2306 if((tPtr->currentTextBlock = tPtr->firstTextBlock))
2307 tPtr->tpos = 0;
2308 updateCursorPosition(tPtr);
2309 paintText(tPtr);
2310 break;
2312 case XK_End:
2313 if((tPtr->currentTextBlock = tPtr->lastTextBlock)) {
2314 if(tPtr->currentTextBlock->graphic)
2315 tPtr->tpos = 1;
2316 else
2317 tPtr->tpos = tPtr->currentTextBlock->used;
2319 updateCursorPosition(tPtr);
2320 paintText(tPtr);
2321 break;
2323 case XK_Left:
2324 if(!(tb = tPtr->currentTextBlock))
2325 break;
2326 if(tb->graphic)
2327 goto L_imaGFX;
2329 if(tPtr->tpos==0) {
2330 L_imaGFX: if(tb->prior) {
2331 tPtr->currentTextBlock = tb->prior;
2332 if(tPtr->currentTextBlock->graphic)
2333 tPtr->tpos = 1;
2334 else
2335 tPtr->tpos = tPtr->currentTextBlock->used;
2337 if(!tb->first && tPtr->tpos > 0)
2338 tPtr->tpos--;
2339 } else tPtr->tpos = 0;
2340 } else tPtr->tpos--;
2341 updateCursorPosition(tPtr);
2342 paintText(tPtr);
2343 break;
2345 case XK_Right:
2346 if(!(tb = tPtr->currentTextBlock))
2347 break;
2348 if(tb->graphic)
2349 goto R_imaGFX;
2350 if(tPtr->tpos == tb->used) {
2351 R_imaGFX: if(tb->next) {
2352 tPtr->currentTextBlock = tb->next;
2353 tPtr->tpos = 0;
2354 if(!tb->next->first && tb->next->used>0)
2355 tPtr->tpos++;
2356 } else {
2357 if(tb->graphic)
2358 tPtr->tpos = 1;
2359 else
2360 tPtr->tpos = tb->used;
2362 } else tPtr->tpos++;
2363 updateCursorPosition(tPtr);
2364 paintText(tPtr);
2365 break;
2367 case XK_Down:
2368 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2369 tPtr->clicked.y + tPtr->cursor.h - tPtr->vpos);
2370 paintText(tPtr);
2371 break;
2373 case XK_Up:
2374 cursorToTextPosition(tPtr, tPtr->cursor.x + tPtr->visible.x,
2375 tPtr->visible.y + tPtr->cursor.y - tPtr->vpos - 3);
2376 paintText(tPtr);
2377 break;
2379 case XK_BackSpace:
2380 case XK_Delete:
2381 #ifdef XK_KP_Delete
2382 case XK_KP_Delete:
2383 #endif
2384 deleteTextInteractively(tPtr, ksym);
2385 updateCursorPosition(tPtr);
2386 paintText(tPtr);
2387 break;
2389 case XK_Control_R :
2390 case XK_Control_L :
2391 control_pressed = True;
2392 break;
2394 case XK_Tab:
2395 insertTextInteractively(tPtr, " ", 4);
2396 updateCursorPosition(tPtr);
2397 paintText(tPtr);
2398 break;
2400 case XK_Return:
2401 *buffer = '\n';
2402 default:
2403 if (*buffer != 0 && !control_pressed) {
2404 insertTextInteractively(tPtr, buffer, strlen(buffer));
2405 updateCursorPosition(tPtr);
2406 paintText(tPtr);
2408 } else if (control_pressed && ksym==XK_r) {
2409 Bool i = !tPtr->flags.rulerShown;
2410 WMShowTextRuler(tPtr, i);
2411 tPtr->flags.rulerShown = i;
2413 else if (control_pressed && *buffer == '\a')
2414 XBell(tPtr->view->screen->display, 0);
2415 else
2416 WMRelayToNextResponder(tPtr->view, event);
2419 if (!control_pressed && tPtr->flags.ownsSelection)
2420 releaseSelection(tPtr);
2424 static void
2425 pasteText(WMView *view, Atom selection, Atom target, Time timestamp,
2426 void *cdata, WMData *data)
2428 Text *tPtr = (Text *)view->self;
2429 char *text;
2431 tPtr->flags.waitingForSelection = 0;
2433 if (data) {
2434 text = (char*)WMDataBytes(data);
2436 if (tPtr->parser) {
2437 (tPtr->parser) (tPtr, (void *) text);
2438 layOutDocument(tPtr);
2439 } else insertTextInteractively(tPtr, text, strlen(text));
2440 updateCursorPosition(tPtr);
2441 paintText(tPtr);
2443 } else {
2444 int n;
2446 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
2448 if (text) {
2449 text[n] = 0;
2450 if (tPtr->parser) {
2451 (tPtr->parser) (tPtr, (void *) text);
2452 layOutDocument(tPtr);
2453 } else insertTextInteractively(tPtr, text, n);
2454 updateCursorPosition(tPtr);
2455 paintText(tPtr);
2457 XFree(text);
2465 static void
2466 handleActionEvents(XEvent *event, void *data)
2468 Text *tPtr = (Text *)data;
2469 Display *dpy = event->xany.display;
2470 KeySym ksym;
2473 switch (event->type) {
2474 case KeyPress:
2475 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2476 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2477 tPtr->flags.extendSelection = True;
2478 return;
2481 if (tPtr->flags.focused) {
2482 XGrabPointer(dpy, W_VIEW(tPtr)->window, False,
2483 PointerMotionMask|ButtonPressMask|ButtonReleaseMask,
2484 GrabModeAsync, GrabModeAsync, None,
2485 tPtr->view->screen->invisibleCursor, CurrentTime);
2486 tPtr->flags.pointerGrabbed = True;
2487 handleTextKeyPress(tPtr, event);
2489 } break;
2491 case KeyRelease:
2492 ksym = XLookupKeysym((XKeyEvent*)event, 0);
2493 if (ksym == XK_Shift_R || ksym == XK_Shift_L) {
2494 tPtr->flags.extendSelection = False;
2495 return;
2496 /* end modify flag so selection can be extended */
2498 break;
2501 case MotionNotify:
2503 if (tPtr->flags.pointerGrabbed) {
2504 tPtr->flags.pointerGrabbed = False;
2505 XUngrabPointer(dpy, CurrentTime);
2508 if(tPtr->flags.waitingForSelection)
2509 break;
2511 if ((event->xmotion.state & Button1Mask)) {
2512 TextBlock *tb = tPtr->currentTextBlock;
2514 if(tb && tPtr->flags.isOverGraphic &&
2515 tb->graphic && !tb->object) {
2516 WMSize offs;
2517 WMPixmap *pixmap = tb->d.pixmap;
2518 char *types[2] = {"application/X-image", NULL};
2520 offs.width = 2;
2521 offs.height = 2;
2523 WMDragImageFromView(tPtr->view, pixmap, types,
2524 wmkpoint(event->xmotion.x_root, event->xmotion.y_root),
2525 offs, event, True);
2528 } else {
2529 if (!tPtr->flags.ownsSelection) {
2530 WMCreateSelectionHandler(tPtr->view,
2531 XA_PRIMARY, event->xbutton.time,
2532 &selectionHandler, NULL);
2533 tPtr->flags.ownsSelection = True;
2536 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2537 break;
2540 mouseOverObject(tPtr, event->xmotion.x, event->xmotion.y);
2541 break;
2544 case ButtonPress:
2546 if (tPtr->flags.pointerGrabbed) {
2547 tPtr->flags.pointerGrabbed = False;
2548 XUngrabPointer(dpy, CurrentTime);
2549 break;
2552 if (tPtr->flags.waitingForSelection)
2553 break;
2555 if (tPtr->flags.extendSelection && tPtr->flags.ownsSelection) {
2556 selectRegion(tPtr, event->xmotion.x, event->xmotion.y);
2557 return;
2560 if (tPtr->flags.ownsSelection)
2561 releaseSelection(tPtr);
2564 if (event->xbutton.button == Button1) {
2566 if(WMIsDoubleClick(event)) {
2567 TextBlock *tb = tPtr->currentTextBlock;
2569 tPtr->lastClickTime = event->xbutton.time;
2570 if(tb && tb->graphic && !tb->object) {
2571 char desc[tb->used+1];
2572 memcpy(desc, tb->text, tb->used);
2573 desc[tb->used] = 0;
2574 if(tPtr->delegate) {
2575 if(tPtr->delegate->didDoubleClickOnPicture)
2576 (*tPtr->delegate->didDoubleClickOnPicture)
2577 (tPtr->delegate, desc);
2579 } else {
2580 autoSelectText(tPtr, 2);
2582 break;
2583 } else if(event->xbutton.time - tPtr->lastClickTime
2584 < WINGsConfiguration.doubleClickDelay) {
2585 tPtr->lastClickTime = event->xbutton.time;
2586 autoSelectText(tPtr, 3);
2587 break;
2590 if (!tPtr->flags.focused) {
2591 WMSetFocusToWidget(tPtr);
2592 tPtr->flags.focused = True;
2595 tPtr->lastClickTime = event->xbutton.time;
2596 cursorToTextPosition(tPtr, event->xmotion.x, event->xmotion.y);
2597 paintText(tPtr);
2600 if (event->xbutton.button
2601 == WINGsConfiguration.mouseWheelDown) {
2602 WMScrollText(tPtr, 16);
2603 break;
2606 if (event->xbutton.button
2607 == WINGsConfiguration.mouseWheelUp) {
2608 WMScrollText(tPtr, -16);
2609 break;
2612 if (event->xbutton.button == Button2) {
2613 char *text = NULL;
2614 int n;
2616 if (!tPtr->flags.editable) {
2617 XBell(dpy, 0);
2618 break;
2621 if (!WMRequestSelection(tPtr->view, XA_PRIMARY, XA_STRING,
2622 event->xbutton.time, pasteText, NULL)) {
2624 text = XFetchBuffer(tPtr->view->screen->display, &n, 0);
2625 tPtr->flags.waitingForSelection = 0;
2627 if (text) {
2628 text[n] = 0;
2630 if (tPtr->parser) {
2631 (tPtr->parser) (tPtr, (void *) text);
2632 layOutDocument(tPtr);
2634 else
2635 insertTextInteractively(tPtr, text, n);
2637 XFree(text);
2638 #if 0
2639 NOTIFY(tPtr, didChange, WMTextDidChangeNotification,
2640 (void*)WMInsertTextEvent);
2641 #endif
2642 updateCursorPosition(tPtr);
2643 paintText(tPtr);
2645 } else {
2646 tPtr->flags.waitingForSelection = True;
2649 break;
2653 case ButtonRelease:
2654 if (tPtr->flags.pointerGrabbed) {
2655 tPtr->flags.pointerGrabbed = False;
2656 XUngrabPointer(dpy, CurrentTime);
2657 break;
2660 if (tPtr->flags.waitingForSelection)
2661 break;
2667 static void
2668 handleEvents(XEvent *event, void *data)
2670 Text *tPtr = (Text *)data;
2672 switch(event->type) {
2673 case Expose:
2675 if (event->xexpose.count!=0)
2676 break;
2678 if(tPtr->hS) {
2679 if (!(W_VIEW(tPtr->hS))->flags.realized)
2680 WMRealizeWidget(tPtr->hS);
2683 if(tPtr->vS) {
2684 if (!(W_VIEW(tPtr->vS))->flags.realized)
2685 WMRealizeWidget(tPtr->vS);
2688 if(tPtr->ruler) {
2689 if (!(W_VIEW(tPtr->ruler))->flags.realized)
2690 WMRealizeWidget(tPtr->ruler);
2694 if(!tPtr->db)
2695 textDidResize(tPtr->view->delegate, tPtr->view);
2697 paintText(tPtr);
2698 break;
2700 case FocusIn:
2701 if (W_FocusedViewOfToplevel(W_TopLevelOfView(tPtr->view))
2702 != tPtr->view)
2703 return;
2704 tPtr->flags.focused = True;
2705 #if DO_BLINK
2706 if (tPtr->flags.editable && !tPtr->timerID) {
2707 tPtr->timerID = WMAddTimerHandler(12+0*CURSOR_BLINK_ON_DELAY,
2708 blinkCursor, tPtr);
2710 #endif
2712 break;
2714 case FocusOut:
2715 tPtr->flags.focused = False;
2716 paintText(tPtr);
2717 #if DO_BLINK
2718 if (tPtr->timerID) {
2719 WMDeleteTimerHandler(tPtr->timerID);
2720 tPtr->timerID = NULL;
2722 #endif
2723 break;
2726 case DestroyNotify:
2727 clearText(tPtr);
2728 if(tPtr->db)
2729 XFreePixmap(tPtr->view->screen->display, tPtr->db);
2730 if(tPtr->gfxItems)
2731 WMEmptyArray(tPtr->gfxItems);
2732 #if DO_BLINK
2733 if (tPtr->timerID)
2734 WMDeleteTimerHandler(tPtr->timerID);
2735 #endif
2736 WMReleaseFont(tPtr->dFont);
2737 WMReleaseColor(tPtr->dColor);
2738 WMDeleteSelectionHandler(tPtr->view, XA_PRIMARY, CurrentTime);
2739 WMRemoveNotificationObserver(tPtr);
2741 wfree(tPtr);
2743 break;
2749 static void
2750 insertPlainText(Text *tPtr, char *text)
2752 char *start, *mark;
2753 void *tb = NULL;
2755 start = text;
2756 while (start) {
2757 mark = strchr(start, '\n');
2758 if (mark) {
2759 tb = WMCreateTextBlockWithText(tPtr,
2760 start, tPtr->dFont,
2761 tPtr->dColor, tPtr->flags.first, (int)(mark-start));
2762 start = mark+1;
2763 tPtr->flags.first = True;
2764 } else {
2765 if (start && strlen(start)) {
2766 tb = WMCreateTextBlockWithText(tPtr, start, tPtr->dFont,
2767 tPtr->dColor, tPtr->flags.first, strlen(start));
2768 } else tb = NULL;
2769 tPtr->flags.first = False;
2770 start = mark;
2773 if (tPtr->flags.prepend)
2774 WMPrependTextBlock(tPtr, tb);
2775 else
2776 WMAppendTextBlock(tPtr, tb);
2781 static void
2782 rulerMoveCallBack(WMWidget *w, void *self)
2784 Text *tPtr = (Text *)self;
2786 if (!tPtr)
2787 return;
2788 if (W_CLASS(tPtr) != WC_Text)
2789 return;
2791 paintText(tPtr);
2795 static void
2796 rulerReleaseCallBack(WMWidget *w, void *self)
2798 Text *tPtr = (Text *)self;
2800 if (!tPtr)
2801 return;
2802 if (W_CLASS(tPtr) != WC_Text)
2803 return;
2805 WMThawText(tPtr);
2806 return;
2809 static unsigned
2810 draggingSourceOperation(WMView *self, Bool local)
2812 return WDOperationCopy;
2815 static WMData*
2816 fetchDragData(WMView *self, char *type)
2818 TextBlock *tb = ((WMText *)self->self)->currentTextBlock;
2819 char *desc;
2820 WMData *data;
2822 if (!tb)
2823 return NULL;
2825 printf("type is [%s]\n", type);
2826 desc = wmalloc(tb->used+1);
2827 memcpy(desc, tb->text, tb->used);
2828 desc[tb->used] = 0;
2829 data = WMCreateDataWithBytes(desc, strlen(desc)+1);
2831 wfree(desc);
2833 return data;
2837 static WMDragSourceProcs _DragSourceProcs = {
2838 draggingSourceOperation,
2839 NULL,
2840 NULL,
2841 fetchDragData
2845 static unsigned
2846 draggingEntered(WMView *self, WMDraggingInfo *info)
2848 printf("draggingEntered\n");
2849 return WDOperationCopy;
2853 static unsigned
2854 draggingUpdated(WMView *self, WMDraggingInfo *info)
2856 return WDOperationCopy;
2860 static void
2861 draggingExited(WMView *self, WMDraggingInfo *info)
2863 printf("draggingExited\n");
2866 static Bool
2867 prepareForDragOperation(WMView *self, WMDraggingInfo *info)
2869 printf("prepareForDragOperation\n");
2870 return True;
2874 char *badbadbad;
2876 static void
2877 receivedData(WMView *view, Atom selection, Atom target, Time timestamp,
2878 void *cdata, WMData *data)
2880 badbadbad = wstrdup((char *)WMDataBytes(data));
2884 /* when it's done in WINGs, remove this */
2886 Bool
2887 requestDroppedData(WMView *view, WMDraggingInfo *info, char *type)
2889 WMScreen *scr = W_VIEW_SCREEN(view);
2891 if (!WMRequestSelection(scr->dragInfo.destView,
2892 scr->xdndSelectionAtom,
2893 XInternAtom(scr->display, type, False),
2894 scr->dragInfo.timestamp,
2895 receivedData, &scr->dragInfo)) {
2896 wwarning("could not request data for dropped data");
2900 XEvent ev;
2902 ev.type = ClientMessage;
2903 ev.xclient.message_type = scr->xdndFinishedAtom;
2904 ev.xclient.format = 32;
2905 ev.xclient.window = info->destinationWindow;
2906 ev.xclient.data.l[0] = 0;
2907 ev.xclient.data.l[1] = 0;
2908 ev.xclient.data.l[2] = 0;
2909 ev.xclient.data.l[3] = 0;
2910 ev.xclient.data.l[4] = 0;
2912 XSendEvent(scr->display, info->sourceWindow, False, 0, &ev);
2913 XFlush(scr->display);
2915 return True;
2918 static Bool
2919 performDragOperation(WMView *self, WMDraggingInfo *info)
2921 WMColor *color;
2922 WMText *tPtr = (WMText *)self->self;
2924 if (!tPtr)
2925 return True;
2927 requestDroppedData(tPtr->view, info, "application/X-color");
2928 color = WMCreateNamedColor(W_VIEW_SCREEN(self), badbadbad, True);
2929 if(color) {
2930 WMSetTextSelectionColor(tPtr, color);
2931 WMReleaseColor(color);
2936 return True;
2939 static void
2940 concludeDragOperation(WMView *self, WMDraggingInfo *info)
2942 printf("concludeDragOperation\n");
2946 static WMDragDestinationProcs _DragDestinationProcs = {
2947 draggingEntered,
2948 draggingUpdated,
2949 draggingExited,
2950 prepareForDragOperation,
2951 performDragOperation,
2952 concludeDragOperation
2956 char *
2957 getStream(WMText *tPtr, int sel, int array)
2959 TextBlock *tb = NULL;
2960 char *text = NULL;
2961 unsigned long where = 0;
2963 if (!tPtr)
2964 return NULL;
2966 if (!(tb = tPtr->firstTextBlock))
2967 return NULL;
2969 if (tPtr->writer) {
2970 (tPtr->writer) (tPtr, (void *) text);
2971 return text;
2974 tb = tPtr->firstTextBlock;
2975 while (tb) {
2977 if (!tb->graphic || (tb->graphic && !tPtr->flags.monoFont)) {
2979 if (!sel || (tb->graphic && tb->selected)) {
2981 if (!tPtr->flags.ignoreNewLine && (tb->first || tb->blank)
2982 && tb != tPtr->firstTextBlock) {
2983 text = wrealloc(text, where+1);
2984 text[where++] = '\n';
2987 if(tb->blank)
2988 goto _gSnext;
2990 if(tb->graphic && array) {
2991 text = wrealloc(text, where+4);
2992 text[where++] = 0xFA;
2993 text[where++] = (tb->used>>8)&0x0ff;
2994 text[where++] = tb->used&0x0ff;
2995 text[where++] = tb->allocated; /* extra info */
2997 text = wrealloc(text, where+tb->used);
2998 memcpy(&text[where], tb->text, tb->used);
2999 where += tb->used;
3002 } else if (sel && tb->selected) {
3004 if (!tPtr->flags.ignoreNewLine && tb->blank) {
3005 text = wrealloc(text, where+1);
3006 text[where++] = '\n';
3009 if(tb->blank)
3010 goto _gSnext;
3012 text = wrealloc(text, where+(tb->s_end - tb->s_begin));
3013 memcpy(&text[where], &tb->text[tb->s_begin],
3014 tb->s_end - tb->s_begin);
3015 where += tb->s_end - tb->s_begin;
3020 _gSnext:tb = tb->next;
3023 /* +1 for the end of string, let's be nice */
3024 text = wrealloc(text, where+1);
3025 text[where] = 0;
3026 return text;
3030 static void
3031 releaseStreamObjects(void *data)
3033 if(data)
3034 wfree(data);
3037 WMArray *
3038 getStreamObjects(WMText *tPtr, int sel)
3040 WMArray *array = WMCreateArrayWithDestructor(4, releaseStreamObjects);
3041 WMData *data;
3042 char *stream;
3043 unsigned short len;
3044 char *start, *fa, *desc;
3046 stream = getStream(tPtr, sel, 1);
3047 if(!stream)
3048 return NULL;
3050 start = stream;
3051 while (start) {
3053 fa = strchr(start, 0xFA);
3054 if (fa) {
3055 if((int)(fa - start)>0) {
3056 desc = start;
3057 desc[(int)(fa - start)] = 0;
3058 data = WMCreateDataWithBytes((void *)desc, (int)(fa - start));
3059 WMSetDataFormat(data, TYPETEXT);
3060 WMAddToArray(array, (void *) data);
3063 len = *(fa+1)*0xff + *(fa+2);
3064 data = WMCreateDataWithBytes((void *)(fa+4), len);
3065 WMSetDataFormat(data, *(fa+3));
3066 WMAddToArray(array, (void *) data);
3067 start = fa + len + 4;
3069 } else {
3070 if (start && strlen(start)) {
3071 data = WMCreateDataWithBytes((void *)start, strlen(start));
3072 WMSetDataFormat(data, TYPETEXT);
3073 WMAddToArray(array, (void *) data);
3075 start = fa;
3079 wfree(stream);
3080 return array;
3084 WMText *
3085 WMCreateTextForDocumentType(WMWidget *parent, WMAction *parser, WMAction *writer)
3087 Text *tPtr;
3088 Display *dpy;
3089 WMScreen *scr;
3090 XGCValues gcv;
3092 tPtr = wmalloc(sizeof(Text));
3093 memset(tPtr, 0, sizeof(Text));
3094 tPtr->widgetClass = WC_Text;
3095 tPtr->view = W_CreateView(W_VIEW(parent));
3096 if (!tPtr->view) {
3097 perror("could not create text's view\n");
3098 wfree(tPtr);
3099 return NULL;
3102 tPtr->view->self = tPtr;
3103 tPtr->view->attribs.cursor = scr->textCursor;
3104 tPtr->view->attribFlags |= CWOverrideRedirect | CWCursor;
3105 W_ResizeView(tPtr->view, 250, 200);
3107 dpy = tPtr->view->screen->display;
3108 scr = tPtr->view->screen;
3110 tPtr->dColor = WMWhiteColor(scr);
3111 tPtr->bgGC = WMColorGC(tPtr->dColor);
3112 W_SetViewBackgroundColor(tPtr->view, tPtr->dColor);
3113 WMReleaseColor(tPtr->dColor);
3115 tPtr->dColor = WMBlackColor(scr);
3116 tPtr->fgGC = WMColorGC(tPtr->dColor);
3118 gcv.graphics_exposures = False;
3119 gcv.foreground = W_PIXEL(scr->gray);
3120 gcv.background = W_PIXEL(scr->darkGray);
3121 gcv.fill_style = FillStippled;
3122 gcv.stipple = XCreateBitmapFromData(dpy, W_DRAWABLE(scr),
3123 STIPPLE_BITS, STIPPLE_WIDTH, STIPPLE_HEIGHT);
3124 tPtr->stippledGC = XCreateGC(dpy, W_DRAWABLE(scr),
3125 GCForeground|GCBackground|GCStipple
3126 |GCFillStyle|GCGraphicsExposures, &gcv);
3128 tPtr->ruler = NULL;
3129 tPtr->vS = NULL;
3130 tPtr->hS = NULL;
3132 tPtr->dFont = WMRetainFont(WMSystemFontOfSize(scr, 12));
3134 tPtr->view->delegate = &_TextViewDelegate;
3136 tPtr->delegate = NULL;
3138 #if DO_BLINK
3139 tPtr->timerID = NULL;
3140 #endif
3142 WMCreateEventHandler(tPtr->view, ExposureMask|StructureNotifyMask
3143 |EnterWindowMask|LeaveWindowMask|FocusChangeMask,
3144 handleEvents, tPtr);
3146 WMCreateEventHandler(tPtr->view, ButtonReleaseMask|ButtonPressMask
3147 |KeyReleaseMask|KeyPressMask|Button1MotionMask,
3148 handleActionEvents, tPtr);
3150 WMAddNotificationObserver(ownershipObserver, tPtr,
3151 "_lostOwnership", tPtr);
3153 WMSetViewDragSourceProcs(tPtr->view, &_DragSourceProcs);
3154 WMSetViewDragDestinationProcs(tPtr->view, &_DragDestinationProcs);
3158 char *types[3] = {"application/X-color", "application/X-image", NULL};
3159 WMRegisterViewForDraggedTypes(tPtr->view, types);
3162 WMAddNotificationObserver(fontChanged, tPtr,
3163 "WMFontPanelDidChangeNotification", tPtr);
3165 tPtr->firstTextBlock = NULL;
3166 tPtr->lastTextBlock = NULL;
3167 tPtr->currentTextBlock = NULL;
3168 tPtr->tpos = 0;
3170 tPtr->gfxItems = WMCreateArray(4);
3172 tPtr->parser = parser;
3173 tPtr->writer = writer;
3175 tPtr->sel.x = tPtr->sel.y = 2;
3176 tPtr->sel.w = tPtr->sel.h = 0;
3178 tPtr->clicked.x = tPtr->clicked.y = 2;
3180 tPtr->visible.x = tPtr->visible.y = 2;
3181 tPtr->visible.h = tPtr->view->size.height;
3182 tPtr->visible.w = tPtr->view->size.width - 4;
3184 tPtr->cursor.x = -23;
3186 tPtr->docWidth = 0;
3187 tPtr->docHeight = 0;
3188 tPtr->dBulletPix = WMCreatePixmapFromXPMData(tPtr->view->screen,
3189 default_bullet);
3190 tPtr->db = (Pixmap) NULL;
3191 tPtr->bgPixmap = NULL;
3193 tPtr->margins = WMGetRulerMargins(NULL);
3194 tPtr->margins->right = tPtr->visible.w;
3195 tPtr->nMargins = 1;
3197 tPtr->flags.rulerShown = False;
3198 tPtr->flags.monoFont = False;
3199 tPtr->flags.focused = False;
3200 tPtr->flags.editable = True;
3201 tPtr->flags.ownsSelection = False;
3202 tPtr->flags.pointerGrabbed = False;
3203 tPtr->flags.extendSelection = False;
3204 tPtr->flags.frozen = False;
3205 tPtr->flags.cursorShown = True;
3206 tPtr->flags.acceptsGraphic = False;
3207 tPtr->flags.horizOnDemand = False;
3208 tPtr->flags.needsLayOut = False;
3209 tPtr->flags.ignoreNewLine = False;
3210 tPtr->flags.indentNewLine = False;
3211 tPtr->flags.laidOut = False;
3212 tPtr->flags.ownsSelection = False;
3213 tPtr->flags.waitingForSelection = False;
3214 tPtr->flags.prepend = False;
3215 tPtr->flags.isOverGraphic = False;
3216 tPtr->flags.relief = WRSunken;
3217 tPtr->flags.isOverGraphic = 0;
3218 tPtr->flags.alignment = WALeft;
3219 tPtr->flags.first = True;
3221 return tPtr;
3224 void
3225 WMPrependTextStream(WMText *tPtr, char *text)
3227 CHECK_CLASS(tPtr, WC_Text);
3229 if(!text) {
3230 if (tPtr->flags.ownsSelection)
3231 releaseSelection(tPtr);
3232 clearText(tPtr);
3233 updateScrollers(tPtr);
3234 return;
3237 tPtr->flags.prepend = True;
3238 if (text && tPtr->parser)
3239 (tPtr->parser) (tPtr, (void *) text);
3240 else
3241 insertPlainText(tPtr, text);
3243 tPtr->flags.needsLayOut = True;
3244 tPtr->tpos = 0;
3245 if(!tPtr->flags.frozen) {
3246 layOutDocument(tPtr);
3251 void
3252 WMAppendTextStream(WMText *tPtr, char *text)
3254 CHECK_CLASS(tPtr, WC_Text);
3256 if(!text) {
3257 if (tPtr->flags.ownsSelection)
3258 releaseSelection(tPtr);
3259 clearText(tPtr);
3260 updateScrollers(tPtr);
3261 return;
3264 tPtr->flags.prepend = False;
3265 if (text && tPtr->parser)
3266 (tPtr->parser) (tPtr, (void *) text);
3267 else
3268 insertPlainText(tPtr, text);
3270 tPtr->flags.needsLayOut = True;
3271 if(tPtr->currentTextBlock) {
3272 if(tPtr->currentTextBlock->graphic)
3273 tPtr->tpos = 1;
3274 else
3275 tPtr->tpos = tPtr->currentTextBlock->used;
3278 if(!tPtr->flags.frozen) {
3279 layOutDocument(tPtr);
3284 char *
3285 WMGetTextStream(WMText *tPtr)
3287 CHECK_CLASS(tPtr, WC_Text);
3288 return getStream(tPtr, 0, 0);
3291 char *
3292 WMGetTextSelectedStream(WMText *tPtr)
3294 CHECK_CLASS(tPtr, WC_Text);
3295 return getStream(tPtr, 1, 0);
3298 WMArray *
3299 WMGetTextObjects(WMText *tPtr)
3301 CHECK_CLASS(tPtr, WC_Text);
3302 return getStreamObjects(tPtr, 0);
3305 WMArray *
3306 WMGetTextSelectedObjects(WMText *tPtr)
3308 CHECK_CLASS(tPtr, WC_Text);
3309 return getStreamObjects(tPtr, 1);
3313 void
3314 WMSetTextDelegate(WMText *tPtr, WMTextDelegate *delegate)
3316 CHECK_CLASS(tPtr, WC_Text);
3318 tPtr->delegate = delegate;
3322 void *
3323 WMCreateTextBlockWithObject(WMText *tPtr, WMWidget *w,
3324 char *description, WMColor *color,
3325 unsigned short first, unsigned short extraInfo)
3327 TextBlock *tb;
3329 if (!w || !description || !color)
3330 return NULL;
3332 tb = wmalloc(sizeof(TextBlock));
3333 if (!tb)
3334 return NULL;
3336 tb->text = wstrdup(description);
3337 tb->used = strlen(description);
3338 tb->blank = False;
3339 tb->d.widget = w;
3340 tb->color = WMRetainColor(color);
3341 tb->marginN = newMargin(tPtr, NULL);
3342 tb->allocated = extraInfo;
3343 tb->first = first;
3344 tb->kanji = False;
3345 tb->graphic = True;
3346 tb->object = True;
3347 tb->underlined = False;
3348 tb->selected = False;
3349 tb->script = 0;
3350 tb->sections = NULL;
3351 tb->nsections = 0;
3352 tb->prior = NULL;
3353 tb->next = NULL;
3355 return tb;
3359 void *
3360 WMCreateTextBlockWithPixmap(WMText *tPtr, WMPixmap *p,
3361 char *description, WMColor *color,
3362 unsigned short first, unsigned short extraInfo)
3364 TextBlock *tb;
3366 if (!p || !description || !color)
3367 return NULL;
3369 tb = wmalloc(sizeof(TextBlock));
3370 if (!tb)
3371 return NULL;
3373 tb->text = wstrdup(description);
3374 tb->used = strlen(description);
3375 tb->blank = False;
3376 tb->d.pixmap = WMRetainPixmap(p);
3377 tb->color = WMRetainColor(color);
3378 tb->marginN = newMargin(tPtr, NULL);
3379 tb->allocated = extraInfo;
3380 tb->first = first;
3381 tb->kanji = False;
3382 tb->graphic = True;
3383 tb->object = False;
3384 tb->underlined = False;
3385 tb->selected = False;
3386 tb->script = 0;
3387 tb->sections = NULL;
3388 tb->nsections = 0;
3389 tb->prior = NULL;
3390 tb->next = NULL;
3392 return tb;
3396 void*
3397 WMCreateTextBlockWithText(WMText *tPtr, char *text, WMFont *font, WMColor *color,
3398 unsigned short first, unsigned short len)
3400 TextBlock *tb;
3402 if (!font || !color)
3403 return NULL;
3405 tb = wmalloc(sizeof(TextBlock));
3406 if (!tb)
3407 return NULL;
3409 tb->allocated = reqBlockSize(len);
3410 tb->text = (char *)wmalloc(tb->allocated);
3411 memset(tb->text, 0, tb->allocated);
3413 if (len < 1|| !text || (*text == '\n' && len==1 )) {
3414 *tb->text = ' ';
3415 tb->used = 1;
3416 tb->blank = True;
3417 } else {
3418 memcpy(tb->text, text, len);
3419 tb->used = len;
3420 tb->blank = False;
3422 tb->text[tb->used] = 0;
3424 tb->d.font = WMRetainFont(font);
3425 tb->color = WMRetainColor(color);
3426 tb->marginN = newMargin(tPtr, NULL);
3427 tb->first = first;
3428 tb->kanji = False;
3429 tb->graphic = False;
3430 tb->underlined = False;
3431 tb->selected = False;
3432 tb->script = 0;
3433 tb->sections = NULL;
3434 tb->nsections = 0;
3435 tb->prior = NULL;
3436 tb->next = NULL;
3437 return tb;
3440 void
3441 WMSetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int first,
3442 unsigned int kanji, unsigned int underlined, int script,
3443 WMRulerMargins *margins)
3445 TextBlock *tb = (TextBlock *) vtb;
3446 if (!tb)
3447 return;
3449 tb->first = first;
3450 tb->kanji = kanji;
3451 tb->underlined = underlined;
3452 tb->script = script;
3453 tb->marginN = newMargin(tPtr, margins);
3456 void
3457 WMGetTextBlockProperties(WMText *tPtr, void *vtb, unsigned int *first,
3458 unsigned int *kanji, unsigned int *underlined, int *script,
3459 WMRulerMargins *margins)
3461 TextBlock *tb = (TextBlock *) vtb;
3462 if (!tb)
3463 return;
3465 if (first) *first = tb->first;
3466 if (kanji) *kanji = tb->kanji;
3467 if (underlined) *underlined = tb->underlined;
3468 if (script) *script = tb->script;
3469 if (margins) margins = &tPtr->margins[tb->marginN];
3474 void
3475 WMPrependTextBlock(WMText *tPtr, void *vtb)
3477 TextBlock *tb = (TextBlock *)vtb;
3479 if (!tPtr || !tb)
3480 return;
3482 if (tb->graphic) {
3483 if(tb->object) {
3484 WMWidget *w = tb->d.widget;
3485 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3486 (W_VIEW(w))->attribs.cursor = tPtr->view->screen->defaultCursor;
3487 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3490 WMAddToArray(tPtr->gfxItems, (void *)tb);
3491 tPtr->tpos = 0;
3492 } else {
3493 if(tb->graphic)
3494 tPtr->tpos = 1;
3495 else
3496 tPtr->tpos = tb->used;
3499 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3500 tb->next = tb->prior = NULL;
3501 tb->first = True;
3502 tPtr->lastTextBlock = tPtr->firstTextBlock
3503 = tPtr->currentTextBlock = tb;
3504 return;
3507 if(!tb->first) {
3508 tb->marginN = tPtr->currentTextBlock->marginN;
3511 tb->next = tPtr->currentTextBlock;
3512 tb->prior = tPtr->currentTextBlock->prior;
3513 if (tPtr->currentTextBlock->prior)
3514 tPtr->currentTextBlock->prior->next = tb;
3516 tPtr->currentTextBlock->prior = tb;
3517 if (!tb->prior)
3518 tPtr->firstTextBlock = tb;
3520 tPtr->currentTextBlock = tb;
3524 void
3525 WMAppendTextBlock(WMText *tPtr, void *vtb)
3527 TextBlock *tb = (TextBlock *)vtb;
3529 if (!tPtr || !tb)
3530 return;
3532 if (tb->graphic) {
3533 if(tb->object) {
3534 WMWidget *w = tb->d.widget;
3535 if (W_CLASS(w) != WC_TextField && W_CLASS(w) != WC_Text) {
3536 (W_VIEW(w))->attribs.cursor =
3537 tPtr->view->screen->defaultCursor;
3538 (W_VIEW(w))->attribFlags |= CWOverrideRedirect | CWCursor;
3541 WMAddToArray(tPtr->gfxItems, (void *)tb);
3542 tPtr->tpos = 0;
3544 } else {
3545 if(tb->graphic)
3546 tPtr->tpos = 1;
3547 else
3548 tPtr->tpos = tb->used;
3552 if (!tPtr->lastTextBlock || !tPtr->firstTextBlock) {
3553 tb->next = tb->prior = NULL;
3554 tb->first = True;
3555 tPtr->lastTextBlock = tPtr->firstTextBlock
3556 = tPtr->currentTextBlock = tb;
3557 return;
3560 if(!tb->first) {
3561 tb->marginN = tPtr->currentTextBlock->marginN;
3564 tb->next = tPtr->currentTextBlock->next;
3565 tb->prior = tPtr->currentTextBlock;
3566 if (tPtr->currentTextBlock->next)
3567 tPtr->currentTextBlock->next->prior = tb;
3569 tPtr->currentTextBlock->next = tb;
3571 if (!tb->next)
3572 tPtr->lastTextBlock = tb;
3574 tPtr->currentTextBlock = tb;
3577 void *
3578 WMRemoveTextBlock(WMText *tPtr)
3580 TextBlock *tb = NULL;
3582 if (!tPtr || !tPtr->firstTextBlock || !tPtr->lastTextBlock
3583 || !tPtr->currentTextBlock) {
3584 return NULL;
3587 tb = tPtr->currentTextBlock;
3588 if (tb->graphic) {
3589 WMRemoveFromArray(tPtr->gfxItems, (void *)tb);
3591 if(tb->object) {
3592 WMUnmapWidget(tb->d.widget);
3596 if (tPtr->currentTextBlock == tPtr->firstTextBlock) {
3597 if (tPtr->currentTextBlock->next)
3598 tPtr->currentTextBlock->next->prior = NULL;
3600 tPtr->firstTextBlock = tPtr->currentTextBlock->next;
3601 tPtr->currentTextBlock = tPtr->firstTextBlock;
3603 } else if (tPtr->currentTextBlock == tPtr->lastTextBlock) {
3604 tPtr->currentTextBlock->prior->next = NULL;
3605 tPtr->lastTextBlock = tPtr->currentTextBlock->prior;
3606 tPtr->currentTextBlock = tPtr->lastTextBlock;
3607 } else {
3608 tPtr->currentTextBlock->prior->next = tPtr->currentTextBlock->next;
3609 tPtr->currentTextBlock->next->prior = tPtr->currentTextBlock->prior;
3610 tPtr->currentTextBlock = tPtr->currentTextBlock->next;
3613 return (void *)tb;
3616 void
3617 WMDestroyTextBlock(WMText *tPtr, void *vtb)
3619 TextBlock *tb = (TextBlock *)vtb;
3620 if (!tPtr || !tb)
3621 return;
3623 if (tb->graphic) {
3624 if(tb->object) {
3625 /* naturally, there's a danger to destroying
3626 widgets whose action brings us here:
3627 ie. press a button to destroy it... need to
3628 find a safer way. till then... this stays commented out */
3629 /* WMDestroyWidget(tb->d.widget);
3630 wfree(tb->d.widget); */
3631 tb->d.widget = NULL;
3632 } else {
3633 WMReleasePixmap(tb->d.pixmap);
3634 tb->d.pixmap = NULL;
3636 } else {
3637 WMReleaseFont(tb->d.font);
3640 WMReleaseColor(tb->color);
3641 if (tb->sections && tb->nsections > 0)
3642 wfree(tb->sections);
3643 wfree(tb->text);
3644 wfree(tb);
3645 tb = NULL;
3650 void
3651 WMSetTextForegroundColor(WMText *tPtr, WMColor *color)
3653 if (!tPtr)
3654 return;
3656 if (color)
3657 tPtr->fgGC = WMColorGC(color);
3658 else {
3659 WMColor *color = WMBlackColor(tPtr->view->screen);
3660 tPtr->fgGC = WMColorGC(color);
3661 WMReleaseColor(color);
3664 paintText(tPtr);
3667 void
3668 WMSetTextBackgroundColor(WMText *tPtr, WMColor *color)
3670 if (!tPtr)
3671 return;
3673 if (color) {
3674 tPtr->bgGC = WMColorGC(color);
3675 W_SetViewBackgroundColor(tPtr->view, color);
3676 } else {
3677 tPtr->bgGC = WMColorGC(WMWhiteColor(tPtr->view->screen));
3678 W_SetViewBackgroundColor(tPtr->view,
3679 WMWhiteColor(tPtr->view->screen));
3682 paintText(tPtr);
3685 void WMSetTextBackgroundPixmap(WMText *tPtr, WMPixmap *pixmap)
3687 if (!tPtr)
3688 return;
3690 if (tPtr->bgPixmap)
3691 WMReleasePixmap(tPtr->bgPixmap);
3693 if (pixmap)
3694 tPtr->bgPixmap = WMRetainPixmap(pixmap);
3695 else
3696 tPtr->bgPixmap = NULL;
3699 void
3700 WMSetTextRelief(WMText *tPtr, WMReliefType relief)
3702 if (!tPtr)
3703 return;
3704 tPtr->flags.relief = relief;
3705 textDidResize(tPtr->view->delegate, tPtr->view);
3708 void
3709 WMSetTextHasHorizontalScroller(WMText *tPtr, Bool shouldhave)
3711 if (!tPtr)
3712 return;
3714 if (shouldhave && !tPtr->hS) {
3715 tPtr->hS = WMCreateScroller(tPtr);
3716 (W_VIEW(tPtr->hS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3717 (W_VIEW(tPtr->hS))->attribFlags |= CWOverrideRedirect | CWCursor;
3718 WMSetScrollerArrowsPosition(tPtr->hS, WSAMinEnd);
3719 WMSetScrollerAction(tPtr->hS, scrollersCallBack, tPtr);
3720 WMMapWidget(tPtr->hS);
3721 } else if (!shouldhave && tPtr->hS) {
3722 WMUnmapWidget(tPtr->hS);
3723 WMDestroyWidget(tPtr->hS);
3724 tPtr->hS = NULL;
3727 tPtr->hpos = 0;
3728 tPtr->prevHpos = 0;
3729 textDidResize(tPtr->view->delegate, tPtr->view);
3733 void
3734 WMSetTextHasRuler(WMText *tPtr, Bool shouldhave)
3736 if (!tPtr)
3737 return;
3739 if(shouldhave && !tPtr->ruler) {
3740 tPtr->ruler = WMCreateRuler(tPtr);
3741 (W_VIEW(tPtr->ruler))->attribs.cursor =
3742 tPtr->view->screen->defaultCursor;
3743 (W_VIEW(tPtr->ruler))->attribFlags |= CWOverrideRedirect | CWCursor;
3744 WMSetRulerReleaseAction(tPtr->ruler, rulerReleaseCallBack, tPtr);
3745 WMSetRulerMoveAction(tPtr->ruler, rulerMoveCallBack, tPtr);
3746 } else if(!shouldhave && tPtr->ruler) {
3747 WMShowTextRuler(tPtr, False);
3748 WMDestroyWidget(tPtr->ruler);
3749 tPtr->ruler = NULL;
3751 textDidResize(tPtr->view->delegate, tPtr->view);
3754 void
3755 WMShowTextRuler(WMText *tPtr, Bool show)
3757 if(!tPtr)
3758 return;
3759 if(!tPtr->ruler)
3760 return;
3762 if(tPtr->flags.monoFont)
3763 show = False;
3765 tPtr->flags.rulerShown = show;
3766 if(show) {
3767 WMMapWidget(tPtr->ruler);
3768 } else {
3769 WMUnmapWidget(tPtr->ruler);
3772 textDidResize(tPtr->view->delegate, tPtr->view);
3775 Bool
3776 WMGetTextRulerShown(WMText *tPtr)
3778 if(!tPtr)
3779 return 0;
3781 if(!tPtr->ruler)
3782 return 0;
3784 return tPtr->flags.rulerShown;
3788 void
3789 WMSetTextHasVerticalScroller(WMText *tPtr, Bool shouldhave)
3791 if (!tPtr)
3792 return;
3794 if (shouldhave && !tPtr->vS) {
3795 tPtr->vS = WMCreateScroller(tPtr);
3796 (W_VIEW(tPtr->vS))->attribs.cursor = tPtr->view->screen->defaultCursor;
3797 (W_VIEW(tPtr->vS))->attribFlags |= CWOverrideRedirect | CWCursor;
3798 WMSetScrollerArrowsPosition(tPtr->vS, WSAMaxEnd);
3799 WMSetScrollerAction(tPtr->vS, scrollersCallBack, tPtr);
3800 WMMapWidget(tPtr->vS);
3801 } else if (!shouldhave && tPtr->vS) {
3802 WMUnmapWidget(tPtr->vS);
3803 WMDestroyWidget(tPtr->vS);
3804 tPtr->vS = NULL;
3807 tPtr->vpos = 0;
3808 tPtr->prevVpos = 0;
3809 textDidResize(tPtr->view->delegate, tPtr->view);
3814 Bool
3815 WMScrollText(WMText *tPtr, int amount)
3817 Bool scroll=False;
3818 if (!tPtr)
3819 return False;
3820 if (amount == 0 || !tPtr->view->flags.realized)
3821 return False;
3823 if (amount < 0) {
3824 if (tPtr->vpos > 0) {
3825 if (tPtr->vpos > abs(amount)) tPtr->vpos += amount;
3826 else tPtr->vpos=0;
3827 scroll=True;
3829 } else {
3830 int limit = tPtr->docHeight - tPtr->visible.h;
3831 if (tPtr->vpos < limit) {
3832 if (tPtr->vpos < limit-amount) tPtr->vpos += amount;
3833 else tPtr->vpos = limit;
3834 scroll = True;
3838 if (scroll && tPtr->vpos != tPtr->prevVpos) {
3839 updateScrollers(tPtr);
3840 paintText(tPtr);
3842 tPtr->prevVpos = tPtr->vpos;
3843 return scroll;
3846 Bool
3847 WMPageText(WMText *tPtr, Bool direction)
3849 if (!tPtr) return False;
3850 if (!tPtr->view->flags.realized) return False;
3852 return WMScrollText(tPtr, direction?tPtr->visible.h:-tPtr->visible.h);
3855 void
3856 WMSetTextEditable(WMText *tPtr, Bool editable)
3858 if (!tPtr)
3859 return;
3860 tPtr->flags.editable = editable;
3863 int
3864 WMGetTextEditable(WMText *tPtr)
3866 if (!tPtr)
3867 return 0;
3868 return tPtr->flags.editable;
3871 void
3872 WMSetTextIndentNewLines(WMText *tPtr, Bool indent)
3874 if (!tPtr)
3875 return;
3876 tPtr->flags.indentNewLine = indent;
3879 void
3880 WMSetTextIgnoresNewline(WMText *tPtr, Bool ignore)
3882 if (!tPtr)
3883 return;
3884 tPtr->flags.ignoreNewLine = ignore;
3887 Bool
3888 WMGetTextIgnoresNewline(WMText *tPtr)
3890 if (!tPtr)
3891 return True;
3892 return tPtr->flags.ignoreNewLine;
3895 void
3896 WMSetTextUsesMonoFont(WMText *tPtr, Bool mono)
3898 if (!tPtr)
3899 return;
3901 if (mono) {
3902 if(tPtr->flags.rulerShown)
3903 WMShowTextRuler(tPtr, False);
3904 if(tPtr->flags.alignment != WALeft)
3905 tPtr->flags.alignment = WALeft;
3908 tPtr->flags.monoFont = mono;
3909 textDidResize(tPtr->view->delegate, tPtr->view);
3912 Bool
3913 WMGetTextUsesMonoFont(WMText *tPtr)
3915 if (!tPtr)
3916 return True;
3917 return tPtr->flags.monoFont;
3921 void
3922 WMSetTextDefaultFont(WMText *tPtr, WMFont *font)
3924 if (!tPtr)
3925 return;
3927 WMReleaseFont(tPtr->dFont);
3928 if (font)
3929 tPtr->dFont = WMRetainFont(font);
3930 else
3931 tPtr->dFont = WMRetainFont(WMSystemFontOfSize(tPtr->view->screen, 12));
3934 WMFont *
3935 WMGetTextDefaultFont(WMText *tPtr)
3937 if (!tPtr)
3938 return NULL;
3939 else
3940 return WMRetainFont(tPtr->dFont);
3943 void
3944 WMSetTextDefaultColor(WMText *tPtr, WMColor *color)
3946 if (!tPtr)
3947 return;
3949 WMReleaseColor(tPtr->dColor);
3950 if (color)
3951 tPtr->dColor = WMRetainColor(color);
3952 else
3953 tPtr->dColor = WMBlackColor(tPtr->view->screen);
3956 WMColor *
3957 WMGetTextDefaultColor(WMText *tPtr)
3959 if (!tPtr)
3960 return NULL;
3961 else
3962 return WMRetainColor(tPtr->dColor);
3965 void
3966 WMSetTextAlignment(WMText *tPtr, WMAlignment alignment)
3968 if (!tPtr)
3969 return;
3970 if(tPtr->flags.monoFont)
3971 tPtr->flags.alignment = WALeft;
3972 else
3973 tPtr->flags.alignment = alignment;
3974 WMThawText(tPtr);
3977 int
3978 WMGetTextInsertType(WMText *tPtr)
3980 if (!tPtr)
3981 return 0;
3982 return tPtr->flags.prepend;
3986 void
3987 WMSetTextSelectionColor(WMText *tPtr, WMColor *color)
3989 if (!tPtr || !color)
3990 return;
3992 setSelectionProperty(tPtr, NULL, color, -1);
3995 WMColor *
3996 WMGetTextSelectionColor(WMText *tPtr)
3998 TextBlock *tb;
4000 if (!tPtr)
4001 return NULL;
4003 tb = tPtr->currentTextBlock;
4005 if (!tb || !tPtr->flags.ownsSelection)
4006 return NULL;
4008 if(!tb->selected)
4009 return NULL;
4011 return tb->color;
4015 void
4016 WMSetTextSelectionFont(WMText *tPtr, WMFont *font)
4018 if (!tPtr || !font)
4019 return;
4021 setSelectionProperty(tPtr, font, NULL, -1) ;
4024 WMFont *
4025 WMGetTextSelectionFont(WMText *tPtr)
4027 TextBlock *tb;
4029 if (!tPtr)
4030 return NULL;
4032 tb = tPtr->currentTextBlock;
4034 if (!tb || !tPtr->flags.ownsSelection)
4035 return NULL;
4037 if(!tb->selected)
4038 return NULL;
4040 if(tb->graphic) {
4041 tb = getFirstNonGraphicBlockFor(tb, 1);
4042 if(!tb)
4043 return NULL;
4045 return (tb->selected ? tb->d.font : NULL);
4049 void
4050 WMSetTextSelectionUnderlined(WMText *tPtr, int underlined)
4052 if (!tPtr || (underlined!=0 && underlined !=1))
4053 return;
4055 setSelectionProperty(tPtr, NULL, NULL, underlined);
4059 int
4060 WMGetTextSelectionUnderlined(WMText *tPtr)
4062 TextBlock *tb;
4064 if (!tPtr)
4065 return 0;
4067 tb = tPtr->currentTextBlock;
4069 if (!tb || !tPtr->flags.ownsSelection)
4070 return 0;
4072 if(!tb->selected)
4073 return 0;
4075 return tb->underlined;
4079 void
4080 WMFreezeText(WMText *tPtr)
4082 if (!tPtr)
4083 return;
4085 tPtr->flags.frozen = True;
4089 void
4090 WMThawText(WMText *tPtr)
4092 if (!tPtr)
4093 return;
4095 tPtr->flags.frozen = False;
4097 if(tPtr->flags.monoFont) {
4098 int j, c = WMGetArrayItemCount(tPtr->gfxItems);
4099 TextBlock *tb;
4101 /* make sure to unmap widgets no matter where they are */
4102 /* they'll be later remapped if needed by paintText */
4103 for(j=0; j<c; j++) {
4104 if ((tb = (TextBlock *) WMGetFromArray(tPtr->gfxItems, j))) {
4105 if (tb->object && ((W_VIEW(tb->d.widget))->flags.mapped))
4106 WMUnmapWidget(tb->d.widget);
4112 tPtr->flags.laidOut = False;
4113 layOutDocument(tPtr);
4114 updateScrollers(tPtr);
4115 paintText(tPtr);
4116 tPtr->flags.needsLayOut = False;
4120 /* find first occurence of a string */
4121 static char *
4122 mystrstr(char *haystack, char *needle, unsigned short len, char *end,
4123 Bool caseSensitive)
4125 char *ptr;
4127 if(!haystack || !needle || !end)
4128 return NULL;
4130 for (ptr = haystack; ptr < end; ptr++) {
4131 if(caseSensitive) {
4132 if (*ptr == *needle && !strncmp(ptr, needle, len))
4133 return ptr;
4135 } else {
4136 if (tolower(*ptr) == tolower(*needle) &&
4137 !strncasecmp(ptr, needle, len))
4138 return ptr;
4142 return NULL;
4145 /* find last occurence of a string */
4146 static char *
4147 mystrrstr(char *haystack, char *needle, unsigned short len, char *end,
4148 Bool caseSensitive)
4150 char *ptr;
4152 if(!haystack || !needle || !end)
4153 return NULL;
4155 for (ptr = haystack-2; ptr > end; ptr--) {
4156 if(caseSensitive) {
4157 if (*ptr == *needle && !strncmp(ptr, needle, len))
4158 return ptr;
4159 } else {
4160 if (tolower(*ptr) == tolower(*needle) &&
4161 !strncasecmp(ptr, needle, len))
4162 return ptr;
4166 return NULL;
4170 Bool
4171 WMFindInTextStream(WMText *tPtr, char *needle, Bool direction,
4172 Bool caseSensitive)
4174 TextBlock *tb;
4175 char *mark=NULL;
4176 unsigned short pos;
4178 if (!tPtr || !needle)
4179 return False;
4181 #if 0
4182 if (! (tb = tPtr->currentTextBlock)) {
4183 if (! (tb = ( (direction > 0) ?
4184 tPtr->firstTextBlock : tPtr->lastTextBlock) ) ){
4185 return False;
4187 } else {
4188 /* if(tb != ((direction>0) ?tPtr->firstTextBlock : tPtr->lastTextBlock))
4189 tb = (direction>0) ? tb->next : tb->prior; */
4190 if(tb != tPtr->lastTextBlock)
4191 tb = tb->prior;
4193 #endif
4194 tb = tPtr->currentTextBlock;
4195 pos = tPtr->tpos;
4198 while(tb) {
4199 if (!tb->graphic) {
4201 if(direction > 0) {
4202 if(pos+1 < tb->used)
4203 pos++;
4205 if(tb->used - pos> 0 && pos > 0) {
4206 mark = mystrstr(&tb->text[pos], needle,
4207 strlen(needle), &tb->text[tb->used], caseSensitive);
4209 } else {
4210 tb = tb->next;
4211 pos = 0;
4212 continue;
4215 } else {
4216 if(pos-1 > 0)
4217 pos--;
4219 if(pos > 0) {
4220 mark = mystrrstr(&tb->text[pos], needle,
4221 strlen(needle), tb->text, caseSensitive);
4222 } else {
4223 tb = tb->prior;
4224 if(!tb)
4225 return False;
4226 pos = tb->used;
4227 continue;
4232 if(mark) {
4233 WMFont *font = tPtr->flags.monoFont?tPtr->dFont:tb->d.font;
4235 tPtr->tpos = (int)(mark - tb->text);
4236 tPtr->currentTextBlock = tb;
4237 updateCursorPosition(tPtr);
4238 tPtr->sel.y = tPtr->cursor.y+5;
4239 tPtr->sel.h = tPtr->cursor.h-10;
4240 tPtr->sel.x = tPtr->cursor.x +1;
4241 tPtr->sel.w = WMIN(WMWidthOfString(font,
4242 &tb->text[tPtr->tpos], strlen(needle)),
4243 tPtr->docWidth - tPtr->sel.x);
4244 tPtr->flags.ownsSelection = True;
4245 paintText(tPtr);
4247 return True;
4251 tb = (direction>0) ? tb->next : tb->prior;
4252 if(tb) {
4253 pos = (direction>0) ? 0 : tb->used;
4257 return False;
4261 Bool
4262 WMReplaceTextSelection(WMText *tPtr, char *replacement)
4264 if (!tPtr)
4265 return False;
4267 if (!tPtr->flags.ownsSelection)
4268 return False;
4270 removeSelection(tPtr);
4272 if(replacement) {
4273 insertTextInteractively(tPtr, replacement, strlen(replacement));
4274 updateCursorPosition(tPtr);
4275 paintText(tPtr);
4278 return True;